Skip to content

Commit

Permalink
Implement Support for no_std
Browse files Browse the repository at this point in the history
This adds the features `std` and `alloc` to the crate. The `std` feature
is activated by default.
  • Loading branch information
CryZe committed Aug 12, 2019
1 parent c1fcfcb commit eaf495a
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 21 deletions.
10 changes: 6 additions & 4 deletions Cargo.toml
Expand Up @@ -6,7 +6,7 @@ description = "encodes and decodes base64 as bytes or utf8"
repository = "https://github.com/alicemaz/rust-base64"
documentation = "https://github.com/alicemaz/rust-base64/blob/master/README.md"
readme = "README.md"
keywords = ["base64", "utf8", "encode", "decode"]
keywords = ["base64", "utf8", "encode", "decode", "no_std"]
categories = ["encoding"]
license = "MIT/Apache-2.0"
edition = "2018"
Expand All @@ -15,14 +15,16 @@ edition = "2018"
name = "benchmarks"
harness = false

[dependencies]
byteorder = "1.2.6"

[dev-dependencies]
criterion = "0.2"
rand = "0.6.1"
doc-comment = "0.3"

[features]
default = ["std"]
alloc = []
std = []

[profile.bench]
# Useful for better disassembly when using `perf record` and `perf annotate`
debug = true
9 changes: 8 additions & 1 deletion src/chunked_encoder.rs
Expand Up @@ -2,7 +2,11 @@ use crate::{
encode::{add_padding, encode_to_slice},
Config,
};
use std::{cmp, str};
#[cfg(any(feature = "alloc", feature = "std", test))]
use alloc::string::String;
use core::cmp;
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::str;

/// The output mechanism for ChunkedEncoder's encoded bytes.
pub trait Sink {
Expand Down Expand Up @@ -80,16 +84,19 @@ fn max_input_length(encoded_buf_len: usize, config: Config) -> usize {
}

// A really simple sink that just appends to a string
#[cfg(any(feature = "alloc", feature = "std", test))]
pub(crate) struct StringSink<'a> {
string: &'a mut String,
}

#[cfg(any(feature = "alloc", feature = "std", test))]
impl<'a> StringSink<'a> {
pub(crate) fn new(s: &mut String) -> StringSink {
StringSink { string: s }
}
}

#[cfg(any(feature = "alloc", feature = "std", test))]
impl<'a> Sink for StringSink<'a> {
type Error = ();

Expand Down
22 changes: 18 additions & 4 deletions src/decode.rs
@@ -1,7 +1,12 @@
use crate::{tables, Config, STANDARD};
use byteorder::{BigEndian, ByteOrder};
use crate::{tables, Config};

use std::{error, fmt, str};
#[cfg(any(feature = "alloc", feature = "std", test))]
use crate::STANDARD;
#[cfg(any(feature = "alloc", feature = "std", test))]
use alloc::vec::Vec;
#[cfg(any(feature = "std", test))]
use std::error;
use core::fmt;

// decode logic operates on chunks of 8 input bytes without padding
const INPUT_CHUNK_LEN: usize = 8;
Expand Down Expand Up @@ -46,6 +51,7 @@ impl fmt::Display for DecodeError {
}
}

#[cfg(any(feature = "std", test))]
impl error::Error for DecodeError {
fn description(&self) -> &str {
match *self {
Expand Down Expand Up @@ -74,6 +80,7 @@ impl error::Error for DecodeError {
/// println!("{:?}", bytes);
///}
///```
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError> {
decode_config(input, STANDARD)
}
Expand All @@ -94,6 +101,7 @@ pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError
/// println!("{:?}", bytes_url);
///}
///```
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
input: &T,
config: Config,
Expand Down Expand Up @@ -124,6 +132,7 @@ pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
/// println!("{:?}", buffer);
///}
///```
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn decode_config_buf<T: ?Sized + AsRef<[u8]>>(
input: &T,
config: Config,
Expand Down Expand Up @@ -419,6 +428,11 @@ fn decode_helper(
Ok(output_index)
}

#[inline]
fn write_u64(output: &mut [u8], value: u64) {
output[..8].copy_from_slice(&value.to_be_bytes());
}

/// Decode 8 bytes of input into 6 bytes of output. 8 bytes of output will be written, but only the
/// first 6 of those contain meaningful data.
///
Expand Down Expand Up @@ -507,7 +521,7 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 16;

BigEndian::write_u64(output, accum);
write_u64(output, accum);

Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions src/display.rs
Expand Up @@ -11,8 +11,8 @@

use super::chunked_encoder::ChunkedEncoder;
use super::Config;
use std::fmt::{Display, Formatter};
use std::{fmt, str};
use core::fmt::{Display, Formatter};
use core::{fmt, str};

/// A convenience wrapper for base64'ing bytes into a format string without heap allocation.
pub struct Base64Display<'a> {
Expand Down
24 changes: 18 additions & 6 deletions src/encode.rs
@@ -1,5 +1,9 @@
use crate::{chunked_encoder, Config, STANDARD};
use byteorder::{BigEndian, ByteOrder};
use crate::Config;
#[cfg(any(feature = "alloc", feature = "std", test))]
use crate::{chunked_encoder, STANDARD};
#[cfg(any(feature = "alloc", feature = "std", test))]
use alloc::{string::String, vec};
use core::convert::TryInto;

///Encode arbitrary octets as base64.
///Returns a String.
Expand All @@ -15,6 +19,7 @@ use byteorder::{BigEndian, ByteOrder};
/// println!("{}", b64);
///}
///```
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
encode_config(input, STANDARD)
}
Expand All @@ -35,6 +40,7 @@ pub fn encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
/// println!("{}", b64_url);
///}
///```
#[cfg(any(feature = "alloc", feature = "std", test))]
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) => vec![0; n],
Expand Down Expand Up @@ -65,6 +71,7 @@ pub fn encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config) -> Stri
/// println!("{}", buf);
///}
///```
#[cfg(any(feature = "alloc", feature = "std", test))]
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 @@ -153,6 +160,11 @@ fn encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output
debug_assert_eq!(encoded_size, encoded_bytes);
}

#[inline]
fn read_u64(s: &[u8]) -> u64 {
u64::from_be_bytes(s[..8].try_into().unwrap())
}

/// Encode input bytes to utf8 base64 bytes. Does not pad.
/// `output` must be long enough to hold the encoded `input` without padding.
/// Returns the number of bytes written.
Expand Down Expand Up @@ -183,7 +195,7 @@ pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64])
// Plus, single-digit percentage performance differences might well be quite different
// on different hardware.

let input_u64 = BigEndian::read_u64(&input_chunk[0..]);
let input_u64 = read_u64(&input_chunk[0..]);

output_chunk[0] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
output_chunk[1] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
Expand All @@ -194,7 +206,7 @@ pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64])
output_chunk[6] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
output_chunk[7] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];

let input_u64 = BigEndian::read_u64(&input_chunk[6..]);
let input_u64 = read_u64(&input_chunk[6..]);

output_chunk[8] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
output_chunk[9] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
Expand All @@ -205,7 +217,7 @@ pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64])
output_chunk[14] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
output_chunk[15] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];

let input_u64 = BigEndian::read_u64(&input_chunk[12..]);
let input_u64 = read_u64(&input_chunk[12..]);

output_chunk[16] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
output_chunk[17] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
Expand All @@ -216,7 +228,7 @@ pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64])
output_chunk[22] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
output_chunk[23] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];

let input_u64 = BigEndian::read_u64(&input_chunk[18..]);
let input_u64 = read_u64(&input_chunk[18..]);

output_chunk[24] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
output_chunk[25] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];
Expand Down
17 changes: 13 additions & 4 deletions src/lib.rs
Expand Up @@ -61,6 +61,12 @@
warnings,
unsafe_code
)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]

#[cfg(all(feature = "alloc", not(any(feature = "std", test))))]
extern crate alloc;
#[cfg(any(feature = "std", test))]
extern crate std as alloc;

#[cfg(test)]
#[macro_use]
Expand All @@ -72,15 +78,18 @@ doctest!("../README.md");
mod chunked_encoder;
pub mod display;
mod tables;
#[cfg(any(feature = "std", test))]
pub mod write;

mod encode;
pub use crate::encode::{encode, encode_config, encode_config_buf, encode_config_slice};
pub use crate::encode::encode_config_slice;
#[cfg(any(feature = "alloc", feature = "std", test))]
pub use crate::encode::{encode, encode_config, encode_config_buf};

mod decode;
pub use crate::decode::{
decode, decode_config, decode_config_buf, decode_config_slice, DecodeError,
};
#[cfg(any(feature = "alloc", feature = "std", test))]
pub use crate::decode::{decode, decode_config, decode_config_buf};
pub use crate::decode::{decode_config_slice, DecodeError};

#[cfg(test)]
mod tests;
Expand Down

0 comments on commit eaf495a

Please sign in to comment.