no_std support

main
Pantonshire 3 years ago
parent 78b940ff19
commit 0b4dbc6bc5

@ -3,5 +3,10 @@ name = "libshire"
version = "0.1.0"
edition = "2021"
[features]
default = ["std", "serde"]
alloc = ["serde?/alloc"]
std = ["serde?/std"]
[dependencies]
serde = { version = "1", default-features = false, optional = true }

@ -1,4 +1,4 @@
use std::convert::Infallible;
use core::convert::Infallible;
/// Consumes an element of type `Infallible` and produces an element of any type `T`. This is
/// possible because `Infallible` has no elements and thus this function can never be called, so

@ -1,9 +1,8 @@
use std::{
use core::{
convert::identity,
fmt,
hint,
ops::{Deref, DerefMut},
process::{ExitCode, Termination},
};
use crate::convert::{clone, clone_mut, copy, copy_mut, Empty};
@ -586,14 +585,15 @@ where
}
}
impl<L, R> Termination for Either<L, R>
#[cfg(feature = "std")]
impl<L, R> std::process::Termination for Either<L, R>
where
L: Termination,
R: Termination,
L: std::process::Termination,
R: std::process::Termination,
{
#[inline]
fn report(self) -> ExitCode {
self.fold(<L as Termination>::report, <R as Termination>::report)
fn report(self) -> std::process::ExitCode {
self.fold(<L as std::process::Termination>::report, <R as std::process::Termination>::report)
}
}

@ -1,5 +1,4 @@
use std::{
error,
use core::{
fmt::{self, Write},
marker::PhantomData,
};
@ -215,7 +214,8 @@ impl fmt::Display for ParseError {
}
}
impl error::Error for ParseError {}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {}
#[derive(Debug)]
pub enum ArrayParseError {
@ -232,7 +232,8 @@ impl fmt::Display for ArrayParseError {
}
}
impl error::Error for ArrayParseError {}
#[cfg(feature = "std")]
impl std::error::Error for ArrayParseError {}
impl From<ParseError> for ArrayParseError {
fn from(err: ParseError) -> Self {
@ -244,8 +245,12 @@ impl From<ParseError> for ArrayParseError {
mod tests {
use super::*;
#[cfg(any(feature = "alloc", feature = "std"))]
#[test]
fn test_hex_bytes_debug() {
#[cfg(not(feature = "std"))]
use alloc::format;
assert_eq!(
format!("{:?}", HexBytes::<Lowercase>::new(&[0x87, 0xe1, 0x8f, 0xaa, 0x88, 0x8d, 0x43, 0x4e, 0xf2, 0xb2, 0x5d, 0xe1, 0xa5, 0x1b, 0xa0, 0x94])),
"[0x87, 0xe1, 0x8f, 0xaa, 0x88, 0x8d, 0x43, 0x4e, 0xf2, 0xb2, 0x5d, 0xe1, 0xa5, 0x1b, 0xa0, 0x94]"
@ -257,8 +262,12 @@ mod tests {
);
}
#[cfg(any(feature = "alloc", feature = "std"))]
#[test]
fn test_hex_bytes_display() {
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
assert_eq!(
HexBytes::<Lowercase>::new(&[0x87, 0xe1, 0x8f, 0xaa, 0x88, 0x8d, 0x43, 0x4e, 0xf2, 0xb2, 0x5d, 0xe1, 0xa5, 0x1b, 0xa0, 0x94]).to_string(),
"87e18faa888d434ef2b25de1a51ba094"

@ -1,3 +1,8 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod convert;
pub mod either;
pub mod hex;

@ -1,13 +1,25 @@
use std::{
borrow::{self, Cow},
use core::{
borrow,
cmp::Ordering,
error,
fmt,
hash::{Hash, Hasher},
ops,
str,
};
#[cfg(not(feature = "std"))]
use core::convert::TryFrom;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
string::String,
};
#[cfg(feature = "std")]
use std::borrow::Cow;
#[derive(Clone)]
pub struct CappedString<const N: usize> {
buf: [u8; N],
@ -97,10 +109,6 @@ impl<const N: usize> CappedString<N> {
unsafe { str::from_utf8_unchecked_mut(slice) }
}
pub fn into_string(self) -> String {
self.as_str().to_owned()
}
pub fn len(&self) -> usize {
usize::from(self.len)
}
@ -110,6 +118,17 @@ impl<const N: usize> CappedString<N> {
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<const N: usize> CappedString<N> {
pub fn into_boxed_str(self) -> Box<str> {
self.as_str().into()
}
pub fn into_string(self) -> String {
self.as_str().to_owned()
}
}
impl<const N: usize> Default for CappedString<N> {
#[inline]
fn default() -> Self {
@ -170,6 +189,7 @@ impl<'a, const N: usize> TryFrom<&'a str> for CappedString<N> {
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<const N: usize> TryFrom<String> for CappedString<N> {
type Error = Error;
@ -179,6 +199,7 @@ impl<const N: usize> TryFrom<String> for CappedString<N> {
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<'a, const N: usize> TryFrom<Cow<'a, str>> for CappedString<N> {
type Error = Error;
@ -188,6 +209,7 @@ impl<'a, const N: usize> TryFrom<Cow<'a, str>> for CappedString<N> {
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<const N: usize> From<CappedString<N>> for String {
#[inline]
fn from(s: CappedString<N>) -> Self {
@ -275,4 +297,5 @@ impl fmt::Display for Error {
}
}
impl error::Error for Error {}
#[cfg(feature = "std")]
impl std::error::Error for Error {}

@ -1,5 +1,5 @@
use std::{
borrow::{self, Cow},
use core::{
borrow,
cmp::Ordering,
convert::Infallible,
fmt,
@ -12,6 +12,16 @@ use std::{
str,
};
#[cfg(not(feature = "std"))]
use alloc::{
borrow::Cow,
boxed::Box,
string::String,
};
#[cfg(feature = "std")]
use std::borrow::Cow;
/// A non-growable string where strings 23 bytes or shorter are stored inline and longer strings
/// use a separate heap allocation. If maximum inline lengths other than 23 are desired, see the
/// more general [InliningString].
@ -274,7 +284,7 @@ impl<const N: usize> InliningString<N> {
#[inline]
#[must_use]
pub fn into_string(self) -> String {
pub fn into_boxed_str(self) -> Box<str> {
match self.inline_string_len() {
Some(len) => {
// Get a pointer to the `inline` field of the union.
@ -296,7 +306,7 @@ impl<const N: usize> InliningString<N> {
// of `InliningString`.
let str_slice = unsafe { str::from_utf8_unchecked(bytes) };
str_slice.to_owned()
Box::from(str_slice)
},
None => {
@ -328,13 +338,18 @@ impl<const N: usize> InliningString<N> {
// The boxed string is initialised, as we obtained it by moving `repr.boxed`, and
// the only time `repr.boxed` is uninitialised is when it is briefly replaced with
// a temporary value in the block above.
let box_str = unsafe { maybe_box_str.assume_init() };
box_str.into_string()
unsafe { maybe_box_str.assume_init() }
},
}
}
#[inline]
#[must_use]
pub fn into_string(self) -> String {
self.into_boxed_str()
.into_string()
}
#[inline]
#[must_use]
pub fn heap_allocated(&self) -> bool {
@ -519,19 +534,26 @@ impl<'de, const N: usize> serde::Deserialize<'de> for InliningString<N> {
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use alloc::{
borrow::{Cow, ToOwned},
vec::Vec,
};
#[cfg(feature = "std")]
use std::borrow::Cow;
use super::*;
#[test]
fn test_align() {
use std::mem::align_of;
use core::mem::align_of;
assert_eq!(align_of::<InliningString23>(), align_of::<Box<str>>());
}
#[test]
fn test_niche() {
use std::mem::size_of;
use core::mem::size_of;
assert_eq!(size_of::<InliningString23>(), size_of::<Option<InliningString23>>());
}

@ -1,7 +1,6 @@
use std::{
use core::{
borrow,
cmp::Ordering,
error,
fmt,
hash::{Hash, Hasher},
ops,
@ -191,7 +190,8 @@ impl fmt::Display for Error {
}
}
impl error::Error for Error {}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[cfg(test)]
mod tests {

@ -1,5 +1,5 @@
use std::{
borrow::{self, Cow},
use core::{
borrow,
cmp::Ordering,
convert::Infallible,
fmt,
@ -8,12 +8,18 @@ use std::{
str::FromStr,
};
#[cfg(not(feature = "std"))]
use alloc::{borrow::Cow, boxed::Box, string::String};
#[cfg(feature = "std")]
use std::borrow::Cow;
use super::CappedString;
/// A non-growable string where strings 22 bytes or shorter are stored inline and longer strings
/// use a separate heap allocation. If maximum inline lengths other than 22 are desired, see the
/// more general [InliningString].
///
///
/// 22 bytes is chosen because it is optimal for 64-bit architectures; the minimum possible size
/// of the data structure on 64-bit architectures which always keeps the data properly aligned is
/// 24 bytes (because, when heap-allocated, the data structure contains a 16-byte `Box<[u8]>` with
@ -24,23 +30,23 @@ pub type InliningString22 = InliningString<22>;
/// A non-growable string which stores small strings inline; strings of length less than or equal
/// to `N` are stored inside the data structure itself, whereas strings of length greater than `N`
/// use a separate heap allocation.
///
///
/// This type is intended to be used when lots of small strings need to be stored, and these
/// strings do not need to grow.
///
///
/// For 64-bit targets, `N = 22` allows the greatest amount of inline string data to be stored
/// without exceeding the size of a regular [String]. Therefore, [InliningString22] is provided as
/// a type alias for `InliningString<22>`.
///
///
/// Although `N` is a `usize`, it may be no greater than `u8::MAX`; larger values will result in a
/// compile-time error.
///
///
/// ```
/// # use libshire::strings::InliningString;
/// let s1 = InliningString::<22>::new("Hello, InliningString!");
/// assert_eq!(&*s1, "Hello, InliningString!");
/// assert!(!s1.heap_allocated());
///
///
/// let s2 = InliningString::<22>::new("This string is 23 bytes");
/// assert_eq!(&*s2, "This string is 23 bytes");
/// assert!(s2.heap_allocated());
@ -51,7 +57,7 @@ pub struct InliningString<const N: usize>(Repr<N>);
impl<const N: usize> InliningString<N> {
/// Creates a new `InliningString` from the given string, storing the string data inline if
/// possible or creating a new heap allocation otherwise.
///
///
/// ```
/// # use libshire::strings::InliningString;
/// let s = InliningString::<22>::new("Hello, InliningString!");
@ -71,7 +77,7 @@ impl<const N: usize> InliningString<N> {
}
/// Returns a new empty `InliningString`.
///
///
/// ```
/// # use libshire::strings::InliningString;
/// let s = InliningString::<22>::empty();
@ -103,6 +109,15 @@ impl<const N: usize> InliningString<N> {
}
}
#[inline]
#[must_use]
pub fn into_boxed_str(self) -> Box<str> {
match self {
Self(Repr::Inline(buf)) => buf.into_boxed_str(),
Self(Repr::Boxed(buf)) => buf,
}
}
/// Consumes the `InliningString` and converts it to a heap-allocated `String`.
#[inline]
#[must_use]
@ -114,7 +129,7 @@ impl<const N: usize> InliningString<N> {
}
/// Returns the length of the string in bytes.
///
///
/// ```
/// # use libshire::strings::InliningString;
/// let s = InliningString::<22>::new("こんにちは");
@ -130,12 +145,12 @@ impl<const N: usize> InliningString<N> {
}
/// Returns `true` if the string has length 0.
///
///
/// ```
/// # use libshire::strings::InliningString;
/// let s1 = InliningString::<22>::new("");
/// assert!(s1.is_empty());
///
///
/// let s2 = InliningString::<22>::new("Hello");
/// assert!(!s2.is_empty());
/// ```
@ -149,12 +164,12 @@ impl<const N: usize> InliningString<N> {
}
/// Returns `true` if the string data is stored on the heap, and `false` otherwise.
///
///
/// ```
/// # use libshire::strings::InliningString;
/// let s1 = InliningString::<22>::new("This string's 22 bytes");
/// assert!(!s1.heap_allocated());
///
///
/// let s2 = InliningString::<22>::new("This string is 23 bytes");
/// assert!(s2.heap_allocated());
/// ```
@ -304,7 +319,7 @@ impl<const N: usize> fmt::Display for InliningString<N> {
impl<const N: usize> serde::Serialize for InliningString<N> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer
S: serde::Serializer,
{
serde::Serialize::serialize(&**self, serializer)
}
@ -314,10 +329,9 @@ impl<const N: usize> serde::Serialize for InliningString<N> {
impl<'de, const N: usize> serde::Deserialize<'de> for InliningString<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>
D: serde::Deserializer<'de>,
{
serde::Deserialize::deserialize(deserializer)
.map(Self::new::<&'de str>)
serde::Deserialize::deserialize(deserializer).map(Self::new::<Cow<'de, str>>)
}
}
@ -329,6 +343,10 @@ enum Repr<const N: usize> {
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use alloc::borrow::{Cow, ToOwned};
#[cfg(feature = "std")]
use std::borrow::Cow;
use super::{InliningString, InliningString22};
@ -341,7 +359,7 @@ mod tests {
"Somethingfortheweekend",
"Dichlorodifluoromethane",
"こんにちは",
"❤️🧡💛💚💙💜"
"❤️🧡💛💚💙💜",
];
for s in test_strings {

@ -1,8 +1,11 @@
#[cfg(any(feature = "alloc", feature = "std"))]
pub mod experimental;
pub mod fixed;
pub mod capped;
#[cfg(any(feature = "alloc", feature = "std"))]
pub mod inlining;
pub use fixed::{FixedString, Error as FixedStringError};
pub use capped::{CappedString, Error as CappedStringError};
#[cfg(any(feature = "alloc", feature = "std"))]
pub use inlining::{InliningString, InliningString22};

@ -1,4 +1,4 @@
use std::{error, fmt, str};
use core::{fmt, str};
use crate::{hex, strings::FixedString};
@ -222,7 +222,8 @@ impl fmt::Display for ParseError {
}
}
impl error::Error for ParseError {}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {}
#[derive(Debug)]
pub enum UuidV5Error {
@ -237,7 +238,8 @@ impl fmt::Display for UuidV5Error {
}
}
impl error::Error for UuidV5Error {}
#[cfg(feature = "std")]
impl std::error::Error for UuidV5Error {}
fn sha1(namespace: [u8; 16], name: &[u8]) -> Result<[u8; 20], UuidV5Error> {
let (mut h0, mut h1, mut h2, mut h3, mut h4) = (
@ -410,8 +412,12 @@ impl<'a> Iterator for Sha1ChunkIter<'a> {
mod tests {
use super::Uuid;
#[cfg(any(feature = "alloc", feature = "std"))]
#[test]
fn test_uuid_display() {
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
assert_eq!(
Uuid::from_bytes([
0x12, 0x3e, 0x45, 0x67, 0xe8, 0x9b, 0x12, 0xd3, 0xa4, 0x56, 0x42, 0x66, 0x14, 0x17,

@ -1,5 +1,9 @@
#!/bin/bash
cargo miri test --no-default-features --features serde
cargo miri test --no-default-features --features alloc,serde
cargo miri test
# 32-bit target

Loading…
Cancel
Save