Skip to content

Commit

Permalink
Merge branch 'master' into mina86-a
Browse files Browse the repository at this point in the history
  • Loading branch information
marshallpierce committed Aug 26, 2023
2 parents d175648 + 70d2b53 commit 2b5112f
Show file tree
Hide file tree
Showing 17 changed files with 739 additions and 167 deletions.
20 changes: 17 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ workflows:
# be easier on the CI hosts since presumably those fat lower layers will already be cached, and
# therefore faster than a minimal, customized alpine.
# MSRV
'rust:1.57.0'
'rust:1.48.0'
]
# a hacky scheme to work around CircleCI's inability to deal with mutable docker tags, forcing us to
# get a nightly or stable toolchain via rustup instead of a mutable docker tag
toolchain_override: [
'__msrv__', # won't add any other toolchains, just uses what's in the docker image
'1.60.0', # minimum needed to build dev-dependencies
'stable',
'beta',
'nightly'
]

Expand Down Expand Up @@ -62,9 +64,16 @@ jobs:
rustup component add clippy
cargo clippy --all-targets
fi
- run:
name: Build main target
command: cargo build
- run:
name: Build all targets
command: cargo build --all-targets
command: |
if [[ '<< parameters.toolchain_override >>' != '__msrv__' ]]
then
cargo build --all-targets
fi
- run:
name: Build without default features
command: cargo build --no-default-features
Expand All @@ -81,8 +90,13 @@ jobs:
name: Build ARM with only alloc feature
command: cargo build --target thumbv6m-none-eabi --no-default-features --features alloc
- run:
# dev dependencies can't build on 1.48.0
name: Run tests
command: cargo test --verbose
command: |
if [[ '<< parameters.toolchain_override >>' != '__msrv__' ]]
then
cargo test --verbose
fi
- run:
name: Build docs
command: cargo doc --verbose
Expand Down
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "base64"
version = "0.21.0"
version = "0.21.2"
authors = ["Alice Maz <alice@alicemaz.com>", "Marshall Pierce <marshall@mpierce.org>"]
description = "encodes and decodes base64 as bytes or utf8"
repository = "https://github.com/marshallpierce/rust-base64"
Expand All @@ -9,13 +9,18 @@ readme = "README.md"
keywords = ["base64", "utf8", "encode", "decode", "no_std"]
categories = ["encoding"]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.57.0"
edition = "2018"
# dev-dependencies require 1.60, but the main code doesn't
# This option was added in 1.56, keep it for when we bump MSRV.
rust-version = "1.48.0"

[[bench]]
name = "benchmarks"
harness = false

[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

[dev-dependencies]
criterion = "0.4.0"
rand = { version = "0.8.5", features = ["small_rng"] }
Expand All @@ -24,6 +29,7 @@ structopt = "0.3.26"
# test fixtures for engine tests
rstest = "0.12.0"
rstest_reuse = "0.3.0"
lazy_static = "1.4.0"

[features]
default = ["std"]
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ optionally may allow other behaviors.

## Rust version compatibility

The minimum supported Rust version is 1.57.0.
The minimum supported Rust version is 1.48.0.

# Contributing

Expand All @@ -76,10 +76,10 @@ free time to give each PR the attention it deserves. I will get to everyone even

## Developing

Benchmarks are in `benches/`. Running them requires nightly rust, but `rustup` makes it easy:
Benchmarks are in `benches/`.

```bash
rustup run nightly cargo bench
cargo bench
```

## no_std
Expand All @@ -92,12 +92,12 @@ to bring back the support for heap allocations.
## Profiling

On Linux, you can use [perf](https://perf.wiki.kernel.org/index.php/Main_Page) for profiling. Then compile the
benchmarks with `rustup nightly run cargo bench --no-run`.
benchmarks with `cargo bench --no-run`.

Run the benchmark binary with `perf` (shown here filtering to one particular benchmark, which will make the results
easier to read). `perf` is only available to the root user on most systems as it fiddles with event counters in your
CPU, so use `sudo`. We need to run the actual benchmark binary, hence the path into `target`. You can see the actual
full path with `rustup run nightly cargo bench -v`; it will print out the commands it runs. If you use the exact path
full path with `cargo bench -v`; it will print out the commands it runs. If you use the exact path
that `bench` outputs, make sure you get the one that's for the benchmarks, not the tests. You may also want
to `cargo clean` so you have only one `benchmarks-` binary (they tend to accumulate).

Expand Down
17 changes: 16 additions & 1 deletion RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
# 0.21.3

- Implement `source` instead of `cause` on Error types
- Roll back MSRV to 1.48.0 so Debian can continue to live in a time warp
- Slightly faster chunked encoding for short inputs

# 0.21.2

- Rollback MSRV to 1.57.0 -- only dev dependencies need 1.60, not the main code

# 0.21.1

- Remove the possibility of panicking during decoded length calculations
- Slightly faster encoding for short inputs
- `DecoderReader` no longer sometimes erroneously ignores padding [#226](https://github.com/marshallpierce/rust-base64/issues/226)

## Breaking changes

- `Engine.internal_decode` return type changed
- Update MSRV to 1.60.0

# 0.21.0

Expand Down
2 changes: 1 addition & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
msrv = "1.57.0"
msrv = "1.48.0"
37 changes: 34 additions & 3 deletions src/alphabet.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Provides [Alphabet] and constants for alphabets commonly used in the wild.

use crate::PAD_BYTE;
use core::fmt;
use core::{convert, fmt};
#[cfg(any(feature = "std", test))]
use std::error;

Expand All @@ -12,13 +12,44 @@ const ALPHABET_SIZE: usize = 64;
/// Common alphabets are provided as constants, and custom alphabets
/// can be made via `from_str` or the `TryFrom<str>` implementation.
///
/// # Examples
///
/// Building and using a custom Alphabet:
///
/// ```
/// let custom = base64::alphabet::Alphabet::new("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").unwrap();
///
/// let engine = base64::engine::GeneralPurpose::new(
/// &custom,
/// base64::engine::general_purpose::PAD);
/// ```
///
/// Building a const:
///
/// ```
/// use base64::alphabet::Alphabet;
///
/// static CUSTOM: Alphabet = {
/// // Result::unwrap() isn't const yet, but panic!() is OK
/// match Alphabet::new("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") {
/// Ok(x) => x,
/// Err(_) => panic!("creation of alphabet failed"),
/// }
/// };
/// ```
///
/// Building a lazy_static:
///
/// ```
/// use base64::{
/// alphabet::Alphabet,
/// engine::{general_purpose::GeneralPurpose, GeneralPurposeConfig},
/// };
///
/// lazy_static::lazy_static! {
/// static ref CUSTOM: Alphabet = Alphabet::new("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").unwrap();
/// }
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Alphabet {
pub(crate) symbols: [u8; ALPHABET_SIZE],
Expand Down Expand Up @@ -93,7 +124,7 @@ impl Alphabet {
}
}

impl TryFrom<&str> for Alphabet {
impl convert::TryFrom<&str> for Alphabet {
type Error = ParseAlphabetError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
Expand Down Expand Up @@ -171,7 +202,7 @@ pub const BIN_HEX: Alphabet = Alphabet::from_str_unchecked(
#[cfg(test)]
mod tests {
use crate::alphabet::*;
use std::convert::TryFrom as _;
use core::convert::TryFrom as _;

#[test]
fn detects_duplicate_start() {
Expand Down
8 changes: 2 additions & 6 deletions src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ impl fmt::Display for DecodeError {
}

#[cfg(any(feature = "std", test))]
impl error::Error for DecodeError {
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
impl error::Error for DecodeError {}

/// Errors that can occur while decoding into a slice.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand All @@ -69,7 +65,7 @@ impl fmt::Display for DecodeSliceError {

#[cfg(any(feature = "std", test))]
impl error::Error for DecodeSliceError {
fn cause(&self) -> Option<&dyn error::Error> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
DecodeSliceError::DecodeError(e) => Some(e),
DecodeSliceError::OutputSliceTooSmall => None,
Expand Down
6 changes: 1 addition & 5 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,7 @@ impl fmt::Display for EncodeSliceError {
}

#[cfg(any(feature = "std", test))]
impl error::Error for EncodeSliceError {
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
impl error::Error for EncodeSliceError {}

#[cfg(test)]
mod tests {
Expand Down
6 changes: 3 additions & 3 deletions src/engine/general_purpose/decode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
engine::{general_purpose::INVALID_VALUE, DecodeEstimate, DecodePaddingMode},
engine::{general_purpose::INVALID_VALUE, DecodeEstimate, DecodeMetadata, DecodePaddingMode},
DecodeError, PAD_BYTE,
};

Expand Down Expand Up @@ -46,7 +46,7 @@ impl DecodeEstimate for GeneralPurposeEstimate {
}

/// Helper to avoid duplicating num_chunks calculation, which is costly on short inputs.
/// Returns the number of bytes written, or an error.
/// Returns the decode metadata, or an error.
// We're on the fragile edge of compiler heuristics here. If this is not inlined, slow. If this is
// inlined(always), a different slow. plain ol' inline makes the benchmarks happiest at the moment,
// but this is fragile and the best setting changes with only minor code modifications.
Expand All @@ -58,7 +58,7 @@ pub(crate) fn decode_helper(
decode_table: &[u8; 256],
decode_allow_trailing_bits: bool,
padding_mode: DecodePaddingMode,
) -> Result<usize, DecodeError> {
) -> Result<DecodeMetadata, DecodeError> {
let remainder_len = input.len() % INPUT_CHUNK_LEN;

// Because the fast decode loop writes in groups of 8 bytes (unrolled to
Expand Down
17 changes: 12 additions & 5 deletions src/engine/general_purpose/decode_suffix.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::{
engine::{general_purpose::INVALID_VALUE, DecodePaddingMode},
engine::{general_purpose::INVALID_VALUE, DecodeMetadata, DecodePaddingMode},
DecodeError, PAD_BYTE,
};

/// Decode the last 1-8 bytes, checking for trailing set bits and padding per the provided
/// parameters.
///
/// Returns the total number of bytes decoded, including the ones indicated as already written by
/// `output_index`.
/// Returns the decode metadata representing the total number of bytes decoded, including the ones
/// indicated as already written by `output_index`.
pub(crate) fn decode_suffix(
input: &[u8],
input_index: usize,
Expand All @@ -16,7 +16,7 @@ pub(crate) fn decode_suffix(
decode_table: &[u8; 256],
decode_allow_trailing_bits: bool,
padding_mode: DecodePaddingMode,
) -> Result<usize, DecodeError> {
) -> Result<DecodeMetadata, DecodeError> {
// Decode any leftovers that aren't a complete input block of 8 bytes.
// Use a u64 as a stack-resident 8 byte buffer.
let mut leftover_bits: u64 = 0;
Expand Down Expand Up @@ -157,5 +157,12 @@ pub(crate) fn decode_suffix(
leftover_bits_appended_to_buf += 8;
}

Ok(output_index)
Ok(DecodeMetadata::new(
output_index,
if padding_bytes > 0 {
Some(input_index + first_padding_index)
} else {
None
},
))
}
5 changes: 3 additions & 2 deletions src/engine/general_purpose/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
use crate::{
alphabet,
alphabet::Alphabet,
engine::{Config, DecodePaddingMode},
engine::{Config, DecodeMetadata, DecodePaddingMode},
DecodeError,
};
use core::convert::TryInto;

mod decode;
pub(crate) mod decode_suffix;

pub use decode::GeneralPurposeEstimate;

pub(crate) const INVALID_VALUE: u8 = 255;
Expand Down Expand Up @@ -170,7 +171,7 @@ impl super::Engine for GeneralPurpose {
input: &[u8],
output: &mut [u8],
estimate: Self::DecodeEstimate,
) -> Result<usize, DecodeError> {
) -> Result<DecodeMetadata, DecodeError> {
decode::decode_helper(
input,
estimate,
Expand Down
Loading

0 comments on commit 2b5112f

Please sign in to comment.