From eaf495a1a2f66cd223ad453d08de459621dbab71 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Mon, 12 Aug 2019 14:32:59 +0200 Subject: [PATCH] Implement Support for no_std This adds the features `std` and `alloc` to the crate. The `std` feature is activated by default. --- Cargo.toml | 10 ++++++---- src/chunked_encoder.rs | 9 ++++++++- src/decode.rs | 22 ++++++++++++++++++---- src/display.rs | 4 ++-- src/encode.rs | 24 ++++++++++++++++++------ src/lib.rs | 17 +++++++++++++---- 6 files changed, 65 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00fd63a0..08dcbdbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" @@ -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 diff --git a/src/chunked_encoder.rs b/src/chunked_encoder.rs index 24cc2d4c..7d405ace 100644 --- a/src/chunked_encoder.rs +++ b/src/chunked_encoder.rs @@ -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 { @@ -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 = (); diff --git a/src/decode.rs b/src/decode.rs index b4208915..d5cf86c6 100644 --- a/src/decode.rs +++ b/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; @@ -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 { @@ -74,6 +80,7 @@ impl error::Error for DecodeError { /// println!("{:?}", bytes); ///} ///``` +#[cfg(any(feature = "alloc", feature = "std", test))] pub fn decode>(input: &T) -> Result, DecodeError> { decode_config(input, STANDARD) } @@ -94,6 +101,7 @@ pub fn decode>(input: &T) -> Result, DecodeError /// println!("{:?}", bytes_url); ///} ///``` +#[cfg(any(feature = "alloc", feature = "std", test))] pub fn decode_config>( input: &T, config: Config, @@ -124,6 +132,7 @@ pub fn decode_config>( /// println!("{:?}", buffer); ///} ///``` +#[cfg(any(feature = "alloc", feature = "std", test))] pub fn decode_config_buf>( input: &T, config: Config, @@ -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. /// @@ -507,7 +521,7 @@ fn decode_chunk( } accum |= (morsel as u64) << 16; - BigEndian::write_u64(output, accum); + write_u64(output, accum); Ok(()) } diff --git a/src/display.rs b/src/display.rs index 3d768294..cc70aac2 100644 --- a/src/display.rs +++ b/src/display.rs @@ -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> { diff --git a/src/encode.rs b/src/encode.rs index 061e9579..ad0aa6ad 100644 --- a/src/encode.rs +++ b/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. @@ -15,6 +19,7 @@ use byteorder::{BigEndian, ByteOrder}; /// println!("{}", b64); ///} ///``` +#[cfg(any(feature = "alloc", feature = "std", test))] pub fn encode>(input: &T) -> String { encode_config(input, STANDARD) } @@ -35,6 +40,7 @@ pub fn encode>(input: &T) -> String { /// println!("{}", b64_url); ///} ///``` +#[cfg(any(feature = "alloc", feature = "std", test))] pub fn encode_config>(input: &T, config: Config) -> String { let mut buf = match encoded_size(input.as_ref().len(), config) { Some(n) => vec![0; n], @@ -65,6 +71,7 @@ pub fn encode_config>(input: &T, config: Config) -> Stri /// println!("{}", buf); ///} ///``` +#[cfg(any(feature = "alloc", feature = "std", test))] pub fn encode_config_buf>(input: &T, config: Config, buf: &mut String) { let input_bytes = input.as_ref(); @@ -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. @@ -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]; @@ -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]; @@ -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]; @@ -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]; diff --git a/src/lib.rs b/src/lib.rs index e72b68c6..0cf6e593 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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] @@ -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;