diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 1b30205..109dd72 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -6,6 +6,11 @@ - Add `BASE32_NOPAD_NOCASE` and `BASE32_NOPAD_VISUAL` +### Patch + +- Add missing safety documentation and assertions for testing and fuzzing +- Move lints from `src/lib.rs` to `Cargo.toml` to ignore MSRV restrictions + ## 2.7.0 ### Minor diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 0371e35..1c37e98 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -21,3 +21,13 @@ rustdoc-args = ["--cfg=docsrs"] default = ["std"] alloc = [] std = ["alloc"] + +[lints] +clippy.undocumented-unsafe-blocks = "warn" +rust.elided-lifetimes-in-paths = "warn" +rust.let-underscore-drop = "warn" +rust.missing-debug-implementations = "warn" +rust.missing-docs = "warn" +rust.unreachable-pub = "warn" +rust.unsafe-op-in-unsafe-fn = "warn" +rust.unused-results = "warn" diff --git a/lib/benches/lib.rs b/lib/benches/lib.rs index a5f5f3f..6a0f1fc 100644 --- a/lib/benches/lib.rs +++ b/lib/benches/lib.rs @@ -1,3 +1,5 @@ +//! Library benchmarks + #![feature(test)] extern crate test; diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 3ec85ed..ee2f5a1 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -145,21 +145,6 @@ #![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -// TODO: This list up to warn(clippy::pedantic) should ideally use a lint group. -#![warn(elided_lifetimes_in_paths)] -// TODO(msrv): #![warn(let_underscore_drop)] -#![warn(missing_debug_implementations)] -#![warn(missing_docs)] -#![warn(unreachable_pub)] -// TODO(msrv): #![warn(unsafe_op_in_unsafe_fn)] -#![warn(unused_results)] -#![allow(unused_unsafe)] // TODO(msrv) -#![warn(clippy::pedantic)] -#![allow(clippy::assigning_clones)] // TODO(msrv) -#![allow(clippy::doc_markdown)] -#![allow(clippy::enum_glob_use)] -#![allow(clippy::similar_names)] -#![allow(clippy::uninlined_format_args)] // TODO(msrv) #[cfg(feature = "alloc")] extern crate alloc; @@ -175,6 +160,7 @@ use alloc::vec; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::convert::TryInto; +use core::debug_assert as safety_assert; macro_rules! check { ($e: expr, $c: expr) => { @@ -255,13 +241,15 @@ macro_rules! dispatch { ($body: expr) => { $body }; } -unsafe fn chunk_unchecked(x: &[T], n: usize, i: usize) -> &[T] { - debug_assert!((i + 1) * n <= x.len()); +fn chunk_unchecked(x: &[T], n: usize, i: usize) -> &[T] { + safety_assert!((i + 1) * n <= x.len()); + // SAFETY: Ensured by correctness requirements (and asserted above). unsafe { core::slice::from_raw_parts(x.as_ptr().add(n * i), n) } } -unsafe fn chunk_mut_unchecked(x: &mut [T], n: usize, i: usize) -> &mut [T] { - debug_assert!((i + 1) * n <= x.len()); +fn chunk_mut_unchecked(x: &mut [T], n: usize, i: usize) -> &mut [T] { + safety_assert!((i + 1) * n <= x.len()); + // SAFETY: Ensured by correctness requirements (and asserted above). unsafe { core::slice::from_raw_parts_mut(x.as_mut_ptr().add(n * i), n) } } @@ -412,8 +400,8 @@ fn encode_mut, M: Static>( _ => 1, }; vectorize(n, bs, |i| { - let input = unsafe { chunk_unchecked(input, enc, i) }; - let output = unsafe { chunk_mut_unchecked(output, dec, i) }; + let input = chunk_unchecked(input, enc, i); + let output = chunk_mut_unchecked(output, dec, i); encode_block(bit, msb, symbols, input, output); }); encode_block(bit, msb, symbols, &input[enc * n ..], &mut output[dec * n ..]); @@ -451,8 +439,8 @@ fn decode_mut, M: Static>( let dec = dec(bit.val()); let n = input.len() / dec; for i in 0 .. n { - let input = unsafe { chunk_unchecked(input, dec, i) }; - let output = unsafe { chunk_mut_unchecked(output, enc, i) }; + let input = chunk_unchecked(input, dec, i); + let output = chunk_mut_unchecked(output, enc, i); decode_block(bit, msb, values, input, output).map_err(|e| dec * i + e)?; } decode_block(bit, msb, values, &input[dec * n ..], &mut output[enc * n ..]) @@ -559,8 +547,8 @@ fn encode_wrap_mut< let olen = dec - end.len(); let n = input.len() / enc; for i in 0 .. n { - let input = unsafe { chunk_unchecked(input, enc, i) }; - let output = unsafe { chunk_mut_unchecked(output, dec, i) }; + let input = chunk_unchecked(input, enc, i); + let output = chunk_mut_unchecked(output, dec, i); encode_base(bit, msb, symbols, input, &mut output[.. olen]); output[olen ..].copy_from_slice(end); } @@ -1324,10 +1312,12 @@ impl Encoding { /// ``` #[cfg(feature = "alloc")] pub fn encode_append(&self, input: &[u8], output: &mut String) { + // SAFETY: Ensured by correctness guarantees of encode_mut (and asserted below). let output = unsafe { output.as_mut_vec() }; let output_len = output.len(); output.resize(output_len + self.encode_len(input.len()), 0u8); self.encode_mut(input, &mut output[output_len ..]); + safety_assert!(output[output_len ..].is_ascii()); } /// Returns an object to encode a fragmented input and append it to `output` @@ -1369,6 +1359,8 @@ impl Encoding { for input in input.chunks(buffer.len() / dec * enc) { let buffer = &mut buffer[.. self.encode_len(input.len())]; self.encode_mut(input, buffer); + safety_assert!(buffer.is_ascii()); + // SAFETY: Ensured by correctness guarantees of encode_mut (and asserted above). output.write_str(unsafe { core::str::from_utf8_unchecked(buffer) })?; } Ok(()) @@ -1403,6 +1395,8 @@ impl Encoding { pub fn encode(&self, input: &[u8]) -> String { let mut output = vec![0u8; self.encode_len(input.len())]; self.encode_mut(input, &mut output); + safety_assert!(output.is_ascii()); + // SAFETY: Ensured by correctness guarantees of encode_mut (and asserted above). unsafe { String::from_utf8_unchecked(output) } } diff --git a/lib/tests/lib.rs b/lib/tests/lib.rs index 9f004de..b1edea9 100644 --- a/lib/tests/lib.rs +++ b/lib/tests/lib.rs @@ -1,3 +1,5 @@ +//! Library tests + use data_encoding::DecodeKind::*; use data_encoding::{DecodeError, Encoding, Specification};