commit c0544cf7d571e5b075953258873953a8451b2097
parent 964b610037e2a8d1104d2840bc2f61d286b29193
Author: Zack Newman <zack@philomathiclife.com>
Date: Fri, 21 Nov 2025 22:30:54 -0700
address clippy for tests
Diffstat:
3 files changed, 54 insertions(+), 39 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -10,7 +10,7 @@ name = "base64url_nopad"
readme = "README.md"
repository = "https://git.philomathiclife.com/repos/base64url_nopad/"
rust-version = "1.89.0"
-version = "0.1.1"
+version = "0.1.2"
[lints.rust]
ambiguous_negative_literals = { level = "deny", priority = -1 }
@@ -23,7 +23,7 @@ future_incompatible = { level = "deny", priority = -1 }
impl_trait_redundant_captures = { level = "deny", priority = -1 }
keyword_idents = { level = "deny", priority = -1 }
let_underscore = { level = "deny", priority = -1 }
-#linker_messages = { level = "deny", priority = -1 }
+linker_messages = { level = "deny", priority = -1 }
#lossy_provenance_casts = { level = "deny", priority = -1 }
macro_use_extern_crate = { level = "deny", priority = -1 }
meta_variable_misuse = { level = "deny", priority = -1 }
@@ -44,9 +44,11 @@ rust_2021_compatibility = { level = "deny", priority = -1 }
rust_2024_compatibility = { level = "deny", priority = -1 }
single_use_lifetimes = { level = "deny", priority = -1 }
#supertrait_item_shadowing_definition = { level = "deny", priority = -1 }
+#supertrait_item_shadowing_usage = { level = "deny", priority = -1 }
trivial_casts = { level = "deny", priority = -1 }
trivial_numeric_casts = { level = "deny", priority = -1 }
unit_bindings = { level = "deny", priority = -1 }
+unknown_or_malformed_diagnostic_attributes = { level = "deny", priority = -1 }
unnameable_types = { level = "deny", priority = -1 }
#unqualified_local_imports = { level = "deny", priority = -1 }
unreachable_pub = { level = "deny", priority = -1 }
@@ -62,7 +64,6 @@ variant_size_differences = { level = "deny", priority = -1 }
warnings = { level = "deny", priority = -1 }
[lints.clippy]
-all = { level = "deny", priority = -1 }
cargo = { level = "deny", priority = -1 }
complexity = { level = "deny", priority = -1 }
correctness = { level = "deny", priority = -1 }
@@ -87,7 +88,19 @@ unseparated_literal_suffix = "allow"
[package.metadata.docs.rs]
all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
+default-target = "x86_64-unknown-linux-gnu"
+targets = [
+ "aarch64-apple-darwin",
+ "aarch64-pc-windows-msvc",
+ "aarch64-unknown-linux-gnu",
+ "i686-pc-windows-msvc",
+ "i686-unknown-linux-gnu",
+ "x86_64-pc-windows-gnu",
+ "x86_64-pc-windows-msvc",
+ "x86_64-unknown-freebsd",
+ "x86_64-unknown-linux-musl",
+ "x86_64-unknown-netbsd"
+]
[dev-dependencies]
rand = { version = "0.9.2", default-features = false, features = ["os_rng", "small_rng"] }
diff --git a/README.md b/README.md
@@ -74,12 +74,13 @@ at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you,
as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
-Before any PR is sent, `cargo clippy` and `cargo t` should be run _for each possible combination of "features"_
-using stable Rust. One easy way to achieve this is by building `ci` and invoking it with no commands in the
-`base64url_nopad` directory or sub-directories. You can fetch `ci` via
-`git clone https://git.philomathiclife.com/repos/ci`, and it can be built with `cargo build --release`.
-Additionally, `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure
-documentation can be built.
+Before any PR is sent, `cargo clippy --all-targets`, `cargo test --all-targets`, and `cargo test --doc` should be
+run _for each possible combination of "features"_ using the stable and MSRV toolchains. One easy way to achieve this
+is by invoking [`ci-cargo`](https://crates.io/crates/ci-cargo) as `ci-cargo clippy --all-targets test --all-targets`
+in the `base64url_nopad` directory.
+
+Last, `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure documentation can be
+built.
### Status
diff --git a/src/lib.rs b/src/lib.rs
@@ -944,20 +944,14 @@ pub const fn encode_len_checked(input_length: usize) -> Option<usize> {
// = 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉
// QED
// Proof of no overflow:
- // usize::MAX >= u16::MAX
- // usize::MAX = 2^i - 1 where i is any integer >= 16 (due to above)
- // Suppose n < 3 * 2^(i-2), then:
- // ⌈4n/3⌉ < ⌈4*(3*2^(i-2))/3⌉ = ⌈2^i⌉
- // = 2^i
- // = usize::MAX + 1
- // thus ignoring intermediate calcuations, the maximum possible value is usize::MAX thus overflow is not
- // possible.
+ // `MAX_ENCODE_INPUT_LEN` = decode_len(usize::MAX).unwrap(); thus all values less than or equal to
+ // `MAX_ENCODE_INPUT_LEN` won't overflow ignoring intermediate calcuations since ⌈4n/3⌉ is a
+ // monotonically increasing function.
// QED
// Naively implementing ⌈4n/3⌉ as (4 * n).div_ceil(3) can cause overflow due to `4 * n`; thus
// we implement the equivalent equation 4⌊n/3⌋ + ⌈4(n mod 3)/3⌉ instead:
// `(4 * (n / 3)) + (4 * (n % 3)).div_ceil(3)` since none of the intermediate calculations suffer
// from overflow.
- // `MAX_ENCODE_INPUT_LEN` = 3 * 2^(i-2) - 1.
if input_length <= MAX_ENCODE_INPUT_LEN {
// (n / 3) << 2u8 <= m <= usize::MAX; thus the left operand of + is fine.
// n % 3 <= 2
@@ -1967,20 +1961,24 @@ mod test {
use super::MAX_ENCODE_INPUT_LEN;
#[cfg(feature = "alloc")]
use alloc::string::String;
- #[cfg(feature = "alloc")]
- use core::fmt;
use rand::{Rng as _, SeedableRng as _, rngs::SmallRng};
+ #[expect(
+ clippy::as_conversions,
+ clippy::cast_possible_truncation,
+ reason = "comment justifies correctness"
+ )]
#[cfg(any(
target_pointer_width = "16",
target_pointer_width = "32",
target_pointer_width = "64",
))]
- #[ignore]
+ #[ignore = "slow"]
#[test]
fn encode_decode_len() {
assert_eq!(MAX_ENCODE_INPUT_LEN, 3 * (usize::MAX.div_ceil(4)) - 1);
let mut rng = SmallRng::from_os_rng();
- for _ in 0..10_000_000 {
+ for _ in 0u32..10_000_000 {
+ // `uN as usize` is fine since we `cfg` by pointer width.
#[cfg(target_pointer_width = "16")]
let len = rng.random::<u16>() as usize;
#[cfg(target_pointer_width = "32")]
@@ -2012,14 +2010,14 @@ mod test {
}
assert!(super::encode_len_checked(usize::MAX).is_none());
assert!(super::encode_len_checked(MAX_ENCODE_INPUT_LEN).is_some_and(|l| l == usize::MAX));
- for _ in 0..10_000_000 {
+ for _ in 0u32..10_000_000 {
#[cfg(target_pointer_width = "16")]
let len = rng.random::<u16>() as usize;
#[cfg(target_pointer_width = "32")]
let len = rng.random::<u32>() as usize;
#[cfg(target_pointer_width = "64")]
let len = rng.random::<u64>() as usize;
- if len % 4 == 1 {
+ if len & 3 == 1 {
assert!(super::decode_len(len).is_none());
} else {
assert!(
@@ -2030,7 +2028,7 @@ mod test {
}
}
for i in 0..1025 {
- if i % 4 == 1 {
+ if i & 3 == 1 {
assert!(super::decode_len(i).is_none());
} else {
assert!(
@@ -2042,7 +2040,7 @@ mod test {
}
#[cfg(target_pointer_width = "16")]
for i in 0..=usize::MAX {
- if i % 4 == 1 {
+ if i & 3 == 1 {
assert!(super::decode_len(i).is_none());
} else {
assert!(super::decode_len(i).is_some_and(|l| super::encode_len_checked(l).is_some_and(|orig| orig == i)));
@@ -2050,7 +2048,7 @@ mod test {
}
#[cfg(not(target_pointer_width = "16"))]
for i in usize::MAX - 1_000_000..=usize::MAX {
- if i % 4 == 1 {
+ if i & 3 == 1 {
assert!(super::decode_len(i).is_none());
} else {
assert!(super::decode_len(i).is_some_and(|l| super::encode_len_checked(l).is_some_and(|orig| orig == i)));
@@ -2058,29 +2056,33 @@ mod test {
}
assert!(super::decode_len(usize::MAX).is_some_and(|l| l == MAX_ENCODE_INPUT_LEN));
}
+ #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
#[cfg(feature = "alloc")]
#[test]
- fn encode_write() -> fmt::Result {
+ fn encode_write() {
let input = [9; 8192];
let mut buffer = String::with_capacity(super::encode_len(input.len()));
- let cap = buffer.capacity() as isize;
+ let cap = buffer.capacity();
let mut write_len;
for len in 0..input.len() {
- write_len = super::encode_len(len) as isize;
- match write_len.checked_add(buffer.len() as isize) {
+ write_len = super::encode_len(len);
+ match write_len.checked_add(buffer.len()) {
None => {
buffer.clear();
- super::encode_write(&input[..len], &mut buffer)?;
- assert_eq!(buffer.len() as isize, write_len);
+ // Indexing is fine since `len <= input.len()`.
+ assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
+ assert_eq!(buffer.len(), write_len);
}
Some(l) => {
if l > cap {
buffer.clear();
- super::encode_write(&input[..len], &mut buffer)?;
- assert_eq!(buffer.len() as isize, write_len);
+ // Indexing is fine since `len <= input.len()`.
+ assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
+ assert_eq!(buffer.len(), write_len);
} else {
- super::encode_write(&input[..len], &mut buffer)?;
- assert_eq!(buffer.len() as isize, l);
+ // Indexing is fine since `len <= input.len()`.
+ assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
+ assert_eq!(buffer.len(), l);
}
}
}
@@ -2091,6 +2093,5 @@ mod test {
.all(|b| { matches!(*b, b'C' | b'J' | b'Q' | b'k') })
);
}
- Ok(())
}
}