diff --git a/Cargo.toml b/Cargo.toml index f43cf071..6b863bce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,16 @@ license = "MIT/Apache-2.0" [dependencies] byteorder = "1.1.0" -safemem = "0.2.0" +safemem = { version = "0.3.0", default-features = false } [dev-dependencies] rand = "0.4" +[features] +std = ["safemem/std"] +alloc = [] +default = ["std"] + [profile.bench] # Useful for better disassembly when using `perf record` and `perf annotate` debug = true diff --git a/README.md b/README.md index d7694dc7..5372ec40 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,18 @@ cargo +nightly fuzz run roundtrip_mime -- -max_len=10240 cargo +nightly fuzz run roundtrip_random_config -- -max_len=10240 ``` +no_std support +--- + +This crate has a feature flag `std` that is enabled by default. + +To use in a *no_std* environment, you can disable default features. To +use the full API, enable feature `alloc` and setup an allocator. + +In environments without an allocator, the API provides only the +`encode_config_slice()` and `decode_config_slice()` functions (which +do not use the dynamically-sized `Vec` type) and the +`strip_whitespace` config must be disabled. License --- diff --git a/src/chunked_encoder.rs b/src/chunked_encoder.rs index e472b073..eb7e5ba6 100644 --- a/src/chunked_encoder.rs +++ b/src/chunked_encoder.rs @@ -1,6 +1,6 @@ use encode::{add_padding, encode_to_slice}; use line_wrap::line_wrap; -use std::cmp; +use core::cmp; use {Config, LineEnding, LineWrap}; /// The output mechanism for ChunkedEncoder's encoded bytes. @@ -171,6 +171,7 @@ pub mod tests { use tests::random_config; use *; + use std::prelude::v1::*; use std::str; use self::rand::distributions::{IndependentSample, Range}; diff --git a/src/decode.rs b/src/decode.rs index e25b752a..c59d2141 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -1,7 +1,13 @@ +#[cfg(feature = "std")] +use std::vec::Vec; +#[cfg(feature = "alloc")] +use alloc::Vec; use byteorder::{BigEndian, ByteOrder}; -use {tables, CharacterSet, Config, STANDARD}; +use {tables, CharacterSet, Config}; -use std::{error, fmt, str}; +use core::fmt; +#[cfg(feature = "std")] +use std::{error, str}; // decode logic operates on chunks of 8 input bytes without padding const INPUT_CHUNK_LEN: usize = 8; @@ -25,6 +31,10 @@ pub enum DecodeError { InvalidByte(usize, u8), /// The length of the input is invalid. InvalidLength, + /// This will be returned if you configure the decoder to strip + /// whitespace but there is not `Vec` in absence of the `alloc` + /// feature. + CannotStripWhitespace, } impl fmt::Display for DecodeError { @@ -34,15 +44,18 @@ impl fmt::Display for DecodeError { write!(f, "Invalid byte {}, offset {}.", byte, index) } DecodeError::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."), + DecodeError::CannotStripWhitespace => write!(f, "Configured to strip white-space but no allocator available"), } } } +#[cfg(feature = "std")] impl error::Error for DecodeError { fn description(&self) -> &str { match *self { DecodeError::InvalidByte(_, _) => "invalid byte", DecodeError::InvalidLength => "invalid length", + DecodeError::CannotStripWhitespace => "cannot strip whitespace", } } @@ -65,7 +78,9 @@ impl error::Error for DecodeError { /// println!("{:?}", bytes); ///} ///``` +#[cfg(any(feature = "std", feature = "alloc"))] pub fn decode>(input: &T) -> Result, DecodeError> { + use STANDARD; decode_config(input, STANDARD) } @@ -85,6 +100,7 @@ pub fn decode>(input: &T) -> Result, DecodeError /// println!("{:?}", bytes_url); ///} ///``` +#[cfg(any(feature = "std", feature = "alloc"))] pub fn decode_config>( input: &T, config: Config, @@ -115,6 +131,7 @@ pub fn decode_config>( /// println!("{:?}", buffer); ///} ///``` +#[cfg(any(feature = "std", feature = "alloc"))] pub fn decode_config_buf>( input: &T, config: Config, @@ -162,10 +179,17 @@ pub fn decode_config_slice>( config: Config, output: &mut [u8], ) -> Result { + #[cfg(any(feature = "std", feature = "alloc"))] let input_copy; let input_bytes = if config.strip_whitespace { - input_copy = copy_without_whitespace(input.as_ref()); - input_copy.as_ref() + #[cfg(not(any(feature = "std", feature = "alloc")))] + return Err(DecodeError::CannotStripWhitespace); + + #[cfg(any(feature = "std", feature = "alloc"))] + { + input_copy = copy_without_whitespace(input.as_ref()); + input_copy.as_ref() + } } else { input.as_ref() }; @@ -186,6 +210,7 @@ fn num_chunks(input: &[u8]) -> usize { .expect("Overflow when calculating number of chunks in input") / INPUT_CHUNK_LEN } +#[cfg(any(feature = "std", feature = "alloc"))] fn copy_without_whitespace(input: &[u8]) -> Vec { let mut input_copy = Vec::::with_capacity(input.len()); input_copy.extend(input.iter().filter(|b| !b" \n\t\r\x0b\x0c".contains(b))); @@ -540,6 +565,7 @@ mod tests { use self::rand::distributions::{IndependentSample, Range}; use self::rand::Rng; + use std::prelude::v1::*; #[test] fn decode_chunk_precise_writes_only_6_bytes() { diff --git a/src/display.rs b/src/display.rs index 53ee1d7b..63d64fb9 100644 --- a/src/display.rs +++ b/src/display.rs @@ -11,8 +11,8 @@ use super::chunked_encoder::{ChunkedEncoder, ChunkedEncoderError}; use super::Config; -use std::fmt::{Display, Formatter}; -use std::{fmt, str}; +use core::fmt::{Display, Formatter}; +use core::{fmt, str}; // I'm not convinced that we should expose ChunkedEncoder or its error type since it's just an // implementation detail, so use a different error type. @@ -82,6 +82,7 @@ mod tests { SinkTestHelper}; use super::super::*; use super::*; + use std::string::String; #[test] fn basic_display() { diff --git a/src/encode.rs b/src/encode.rs index be1eb86a..ca4a9d65 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -1,5 +1,9 @@ +#[cfg(feature = "std")] +use std::string::String; +#[cfg(feature = "alloc")] +use alloc::String; use byteorder::{BigEndian, ByteOrder}; -use {line_wrap, line_wrap_parameters, Config, LineWrap, STANDARD}; +use {line_wrap, line_wrap_parameters, Config, LineWrap}; ///Encode arbitrary octets as base64. ///Returns a String. @@ -15,7 +19,9 @@ use {line_wrap, line_wrap_parameters, Config, LineWrap, STANDARD}; /// println!("{}", b64); ///} ///``` +#[cfg(any(feature = "std", feature = "alloc"))] pub fn encode>(input: &T) -> String { + use STANDARD; encode_config(input, STANDARD) } @@ -35,6 +41,7 @@ pub fn encode>(input: &T) -> String { /// println!("{}", b64_url); ///} ///``` +#[cfg(any(feature = "std", feature = "alloc"))] pub fn encode_config>(input: &T, config: Config) -> String { let mut buf = match encoded_size(input.as_ref().len(), &config) { Some(n) => String::with_capacity(n), @@ -64,6 +71,7 @@ pub fn encode_config>(input: &T, config: Config) -> Stri /// println!("{}", buf); ///} ///``` +#[cfg(any(feature = "std", feature = "alloc"))] pub fn encode_config_buf>(input: &T, config: Config, buf: &mut String) { let input_bytes = input.as_ref(); @@ -354,6 +362,7 @@ mod tests { use self::rand::distributions::{IndependentSample, Range}; use self::rand::Rng; use std; + use std::prelude::v1::*; use std::str; #[test] diff --git a/src/lib.rs b/src/lib.rs index 020592fc..e730c2e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,15 @@ unused_results, variant_size_differences, warnings )] +#![cfg_attr(feature = "alloc", feature(alloc))] +#![no_std] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[cfg(feature = "alloc")] +extern crate alloc; + extern crate byteorder; mod chunked_encoder; @@ -70,10 +79,14 @@ mod tables; use line_wrap::{line_wrap, line_wrap_parameters}; mod encode; -pub use encode::{encode, encode_config, encode_config_buf, encode_config_slice}; +pub use encode::encode_config_slice; +#[cfg(any(feature = "std", feature = "alloc"))] +pub use encode::{encode, encode_config, encode_config_buf}; mod decode; -pub use decode::{decode, decode_config, decode_config_buf, decode_config_slice, DecodeError}; +pub use decode::{decode_config_slice, DecodeError}; +#[cfg(any(feature = "std", feature = "alloc"))] +pub use decode::{decode, decode_config, decode_config_buf}; #[cfg(test)] mod tests; diff --git a/src/line_wrap.rs b/src/line_wrap.rs index ca81be96..80be5987 100644 --- a/src/line_wrap.rs +++ b/src/line_wrap.rs @@ -165,6 +165,7 @@ mod tests { use self::rand::distributions::{IndependentSample, Range}; use self::rand::Rng; + use std::prelude::v1::*; #[test] fn line_params_perfect_multiple_of_line_length_lf() { diff --git a/src/tests.rs b/src/tests.rs index ff0f4b67..5c50f5f8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,6 +4,7 @@ use encode::encoded_size; use line_wrap::line_wrap_parameters; use *; +use std::prelude::v1::*; use std::str; use self::rand::distributions::{IndependentSample, Range};