Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no_std support #62

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion Cargo.toml
Expand Up @@ -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
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -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.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unpleasant interplay of features is yet more evidence to me that line wrapping (and its dual, whitespace stripping) are a mistake to include, at least at this level. Maybe a base64::ext should contain helper functions to do that stuff after the fact. This would also simplify #56.


License
---
Expand Down
3 changes: 2 additions & 1 deletion 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.
Expand Down Expand Up @@ -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};
Expand Down
34 changes: 30 additions & 4 deletions 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;
Expand All @@ -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 {
Expand All @@ -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",
}
}

Expand All @@ -65,7 +78,9 @@ impl error::Error for DecodeError {
/// println!("{:?}", bytes);
///}
///```
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError> {
use STANDARD;
decode_config(input, STANDARD)
}

Expand All @@ -85,6 +100,7 @@ pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError
/// println!("{:?}", bytes_url);
///}
///```
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
input: &T,
config: Config,
Expand Down Expand Up @@ -115,6 +131,7 @@ pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
/// println!("{:?}", buffer);
///}
///```
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn decode_config_buf<T: ?Sized + AsRef<[u8]>>(
input: &T,
config: Config,
Expand Down Expand Up @@ -162,10 +179,17 @@ pub fn decode_config_slice<T: ?Sized + AsRef<[u8]>>(
config: Config,
output: &mut [u8],
) -> Result<usize, DecodeError> {
#[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()
};
Expand All @@ -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<u8> {
let mut input_copy = Vec::<u8>::with_capacity(input.len());
input_copy.extend(input.iter().filter(|b| !b" \n\t\r\x0b\x0c".contains(b)));
Expand Down Expand Up @@ -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() {
Expand Down
5 changes: 3 additions & 2 deletions src/display.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -82,6 +82,7 @@ mod tests {
SinkTestHelper};
use super::super::*;
use super::*;
use std::string::String;

#[test]
fn basic_display() {
Expand Down
11 changes: 10 additions & 1 deletion 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.
Expand All @@ -15,7 +19,9 @@ use {line_wrap, line_wrap_parameters, Config, LineWrap, STANDARD};
/// println!("{}", b64);
///}
///```
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
use STANDARD;
encode_config(input, STANDARD)
}

Expand All @@ -35,6 +41,7 @@ pub fn encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
/// println!("{}", b64_url);
///}
///```
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config) -> String {
let mut buf = match encoded_size(input.as_ref().len(), &config) {
Some(n) => String::with_capacity(n),
Expand Down Expand Up @@ -64,6 +71,7 @@ pub fn encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config) -> Stri
/// println!("{}", buf);
///}
///```
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn encode_config_buf<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config, buf: &mut String) {
let input_bytes = input.as_ref();

Expand Down Expand Up @@ -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]
Expand Down
17 changes: 15 additions & 2 deletions src/lib.rs
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/line_wrap.rs
Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions src/tests.rs
Expand Up @@ -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};
Expand Down