Skip to content

Commit a40d420

Browse files
Merge pull request #111 from CryZe/no-std
Implement Support for no_std
2 parents d4cd64f + b4da176 commit a40d420

File tree

9 files changed

+84
-26
lines changed

9 files changed

+84
-26
lines changed

.travis.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ sudo: required
55

66
matrix:
77
include:
8-
- rust: 1.31.0
8+
- rust: 1.34.0
99
- rust: stable
1010
- rust: beta
1111
- rust: nightly
@@ -19,16 +19,23 @@ matrix:
1919
- cargo tarpaulin --version || RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --force cargo-tarpaulin
2020
- cargo fuzz --version || cargo install --force cargo-fuzz
2121

22+
# no_std
23+
- rust: stable
24+
env: TARGET="--target thumbv6m-none-eabi" FEATURES="--no-default-features --features alloc"
25+
install:
26+
- rustup target add thumbv6m-none-eabi
27+
2228
cache: cargo
2329

2430
env:
2531
# prevent cargo fuzz list from printing with color
2632
- TERM=dumb
2733

2834
script:
29-
- cargo build --verbose
30-
- cargo test --verbose
31-
- cargo doc --verbose
35+
- cargo build --verbose $TARGET --no-default-features
36+
- cargo build --verbose $TARGET $FEATURES
37+
- 'if [[ -z "$TARGET" ]]; then cargo test --verbose; fi'
38+
- 'if [[ -z "$TARGET" ]]; then cargo doc --verbose; fi'
3239
- 'if [[ "$TRAVIS_RUST_VERSION" = nightly ]]; then cargo bench --no-run; fi'
3340
# run for just a second to confirm that it can build and run ok
3441
- 'if [[ "$TRAVIS_RUST_VERSION" = nightly ]]; then cargo fuzz list | xargs -L 1 -I FUZZER cargo fuzz run FUZZER -- -max_total_time=1; fi'

Cargo.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description = "encodes and decodes base64 as bytes or utf8"
66
repository = "https://github.com/marshallpierce/rust-base64"
77
documentation = "https://docs.rs/base64"
88
readme = "README.md"
9-
keywords = ["base64", "utf8", "encode", "decode"]
9+
keywords = ["base64", "utf8", "encode", "decode", "no_std"]
1010
categories = ["encoding"]
1111
license = "MIT/Apache-2.0"
1212
edition = "2018"
@@ -15,14 +15,16 @@ edition = "2018"
1515
name = "benchmarks"
1616
harness = false
1717

18-
[dependencies]
19-
byteorder = "1.2.6"
20-
2118
[dev-dependencies]
2219
criterion = "0.2"
2320
rand = "0.6.1"
2421
doc-comment = "0.3"
2522

23+
[features]
24+
default = ["std"]
25+
alloc = []
26+
std = []
27+
2628
[profile.bench]
2729
# Useful for better disassembly when using `perf record` and `perf annotate`
2830
debug = true

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ See the [docs](https://docs.rs/base64) for all the details.
3333
Rust version compatibility
3434
---
3535

36-
The minimum required Rust version is 1.31.0.
36+
The minimum required Rust version is 1.34.0.
3737

3838
Developing
3939
---
@@ -50,6 +50,11 @@ Decoding is aided by some pre-calculated tables, which are generated by:
5050
cargo run --example make_tables > src/tables.rs.tmp && mv src/tables.rs.tmp src/tables.rs
5151
```
5252

53+
no_std
54+
---
55+
56+
This crate supports no_std. By default the crate targets std via the `std` feature. You can deactivate the `default-features` to target core instead. In that case you lose out on all the functionality revolving around `std::io`, `std::error::Error` and heap allocations. There is an additional `alloc` feature that you can activate to bring back the support for heap allocations.
57+
5358
Profiling
5459
---
5560

RELEASE-NOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Next
22

33
- TBD
4+
- Minimum rust version 1.34.0
5+
- `no_std` is now supported via the two new features `alloc` and `std`.
46

57
# 0.10.1
68

src/chunked_encoder.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use crate::{
22
encode::{add_padding, encode_to_slice},
33
Config,
44
};
5-
use std::{cmp, str};
5+
#[cfg(any(feature = "alloc", feature = "std", test))]
6+
use alloc::string::String;
7+
use core::cmp;
8+
#[cfg(any(feature = "alloc", feature = "std", test))]
9+
use core::str;
610

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

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

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

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

src/decode.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
use crate::{tables, Config, STANDARD};
2-
use byteorder::{BigEndian, ByteOrder};
1+
use crate::{tables, Config};
32

4-
use std::{error, fmt, str};
3+
#[cfg(any(feature = "alloc", feature = "std", test))]
4+
use crate::STANDARD;
5+
#[cfg(any(feature = "alloc", feature = "std", test))]
6+
use alloc::vec::Vec;
7+
#[cfg(any(feature = "std", test))]
8+
use std::error;
9+
use core::fmt;
510

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

54+
#[cfg(any(feature = "std", test))]
4955
impl error::Error for DecodeError {
5056
fn description(&self) -> &str {
5157
match *self {
@@ -74,6 +80,7 @@ impl error::Error for DecodeError {
7480
/// println!("{:?}", bytes);
7581
///}
7682
///```
83+
#[cfg(any(feature = "alloc", feature = "std", test))]
7784
pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError> {
7885
decode_config(input, STANDARD)
7986
}
@@ -94,6 +101,7 @@ pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError
94101
/// println!("{:?}", bytes_url);
95102
///}
96103
///```
104+
#[cfg(any(feature = "alloc", feature = "std", test))]
97105
pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
98106
input: &T,
99107
config: Config,
@@ -124,6 +132,7 @@ pub fn decode_config<T: ?Sized + AsRef<[u8]>>(
124132
/// println!("{:?}", buffer);
125133
///}
126134
///```
135+
#[cfg(any(feature = "alloc", feature = "std", test))]
127136
pub fn decode_config_buf<T: ?Sized + AsRef<[u8]>>(
128137
input: &T,
129138
config: Config,
@@ -419,6 +428,11 @@ fn decode_helper(
419428
Ok(output_index)
420429
}
421430

431+
#[inline]
432+
fn write_u64(output: &mut [u8], value: u64) {
433+
output[..8].copy_from_slice(&value.to_be_bytes());
434+
}
435+
422436
/// Decode 8 bytes of input into 6 bytes of output. 8 bytes of output will be written, but only the
423437
/// first 6 of those contain meaningful data.
424438
///
@@ -507,7 +521,7 @@ fn decode_chunk(
507521
}
508522
accum |= (morsel as u64) << 16;
509523

510-
BigEndian::write_u64(output, accum);
524+
write_u64(output, accum);
511525

512526
Ok(())
513527
}

src/display.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
1212
use super::chunked_encoder::ChunkedEncoder;
1313
use super::Config;
14-
use std::fmt::{Display, Formatter};
15-
use std::{fmt, str};
14+
use core::fmt::{Display, Formatter};
15+
use core::{fmt, str};
1616

1717
/// A convenience wrapper for base64'ing bytes into a format string without heap allocation.
1818
pub struct Base64Display<'a> {

src/encode.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
use crate::{chunked_encoder, Config, STANDARD};
2-
use byteorder::{BigEndian, ByteOrder};
1+
use crate::Config;
2+
#[cfg(any(feature = "alloc", feature = "std", test))]
3+
use crate::{chunked_encoder, STANDARD};
4+
#[cfg(any(feature = "alloc", feature = "std", test))]
5+
use alloc::{string::String, vec};
6+
use core::convert::TryInto;
37

48
///Encode arbitrary octets as base64.
59
///Returns a String.
@@ -15,6 +19,7 @@ use byteorder::{BigEndian, ByteOrder};
1519
/// println!("{}", b64);
1620
///}
1721
///```
22+
#[cfg(any(feature = "alloc", feature = "std", test))]
1823
pub fn encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
1924
encode_config(input, STANDARD)
2025
}
@@ -35,6 +40,7 @@ pub fn encode<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
3540
/// println!("{}", b64_url);
3641
///}
3742
///```
43+
#[cfg(any(feature = "alloc", feature = "std", test))]
3844
pub fn encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config) -> String {
3945
let mut buf = match encoded_size(input.as_ref().len(), config) {
4046
Some(n) => vec![0; n],
@@ -65,6 +71,7 @@ pub fn encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config) -> Stri
6571
/// println!("{}", buf);
6672
///}
6773
///```
74+
#[cfg(any(feature = "alloc", feature = "std", test))]
6875
pub fn encode_config_buf<T: ?Sized + AsRef<[u8]>>(input: &T, config: Config, buf: &mut String) {
6976
let input_bytes = input.as_ref();
7077

@@ -153,6 +160,11 @@ fn encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output
153160
debug_assert_eq!(encoded_size, encoded_bytes);
154161
}
155162

163+
#[inline]
164+
fn read_u64(s: &[u8]) -> u64 {
165+
u64::from_be_bytes(s[..8].try_into().unwrap())
166+
}
167+
156168
/// Encode input bytes to utf8 base64 bytes. Does not pad.
157169
/// `output` must be long enough to hold the encoded `input` without padding.
158170
/// Returns the number of bytes written.
@@ -183,7 +195,7 @@ pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64])
183195
// Plus, single-digit percentage performance differences might well be quite different
184196
// on different hardware.
185197

186-
let input_u64 = BigEndian::read_u64(&input_chunk[0..]);
198+
let input_u64 = read_u64(&input_chunk[0..]);
187199

188200
output_chunk[0] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
189201
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])
194206
output_chunk[6] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
195207
output_chunk[7] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
196208

197-
let input_u64 = BigEndian::read_u64(&input_chunk[6..]);
209+
let input_u64 = read_u64(&input_chunk[6..]);
198210

199211
output_chunk[8] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
200212
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])
205217
output_chunk[14] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
206218
output_chunk[15] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
207219

208-
let input_u64 = BigEndian::read_u64(&input_chunk[12..]);
220+
let input_u64 = read_u64(&input_chunk[12..]);
209221

210222
output_chunk[16] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
211223
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])
216228
output_chunk[22] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize];
217229
output_chunk[23] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize];
218230

219-
let input_u64 = BigEndian::read_u64(&input_chunk[18..]);
231+
let input_u64 = read_u64(&input_chunk[18..]);
220232

221233
output_chunk[24] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize];
222234
output_chunk[25] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize];

src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
warnings
6262
)]
6363
#![forbid(unsafe_code)]
64+
#![cfg_attr(not(any(feature = "std", test)), no_std)]
65+
66+
#[cfg(all(feature = "alloc", not(any(feature = "std", test))))]
67+
extern crate alloc;
68+
#[cfg(any(feature = "std", test))]
69+
extern crate std as alloc;
6470

6571
#[cfg(test)]
6672
#[macro_use]
@@ -72,15 +78,18 @@ doctest!("../README.md");
7278
mod chunked_encoder;
7379
pub mod display;
7480
mod tables;
81+
#[cfg(any(feature = "std", test))]
7582
pub mod write;
7683

7784
mod encode;
78-
pub use crate::encode::{encode, encode_config, encode_config_buf, encode_config_slice};
85+
pub use crate::encode::encode_config_slice;
86+
#[cfg(any(feature = "alloc", feature = "std", test))]
87+
pub use crate::encode::{encode, encode_config, encode_config_buf};
7988

8089
mod decode;
81-
pub use crate::decode::{
82-
decode, decode_config, decode_config_buf, decode_config_slice, DecodeError,
83-
};
90+
#[cfg(any(feature = "alloc", feature = "std", test))]
91+
pub use crate::decode::{decode, decode_config, decode_config_buf};
92+
pub use crate::decode::{decode_config_slice, DecodeError};
8493

8594
#[cfg(test)]
8695
mod tests;

0 commit comments

Comments
 (0)