Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,7 @@ jobs:
with:
submodules: true
- uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
- uses: dtolnay/rust-toolchain@nightly
with:
profile: minimal
- uses: taiki-e/install-action@cargo-llvm-cov
- run: cargo llvm-cov --all-features --workspace --codecov --output-path codecov.json
- uses: codecov/codecov-action@v3
Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use core::{fmt, ops::Deref, str::FromStr};
use arrayvec::ArrayString;

mod util;
#[cfg(feature = "rand")]
use util::StrExt as _;
use util::{digits, ChunksExt as _, IteratorExt as _};

include!(concat!(env!("OUT_DIR"), "/countries.rs"));
Expand Down Expand Up @@ -451,15 +453,13 @@ impl Iban {
debug_assert_eq!(iban.len(), expected_length);

let check_digits = 98 - calculate_checksum(iban.as_bytes());
#[allow(clippy::cast_possible_truncation)]
let check_digits = [
b'0' + (check_digits / 10) as u8,
b'0' + (check_digits % 10) as u8,
];

// TODO: Figure out a way to swap out the characters without unsafe.
// SAFETY: All of the characters generated are ASCII, so there are no issues with character boundries.
unsafe { &mut iban.as_bytes_mut()[2..4] }.copy_from_slice(&check_digits);
let check_digits = core::str::from_utf8(&check_digits)
.expect("check digits are ASCII, and by definition valid UTF-8");
iban[2..4].copy_from_str(check_digits);

Ok(Self(iban))
}
Expand Down
21 changes: 21 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,24 @@ pub fn digits(mut value: u8) -> impl Iterator<Item = u8> {
// Ensure at least one value (0) is provided by this iterator.
.ensure_one(0)
}

#[cfg(feature = "rand")]
pub trait StrExt {
/// Copies the contents of `src` into `self`, using a memcpy.
///
/// The length of `src` must be the same as `self`.
///
/// # Panics
///
/// This function will panic if the two str have different lengths.
fn copy_from_str(&mut self, src: &str);
}

#[cfg(feature = "rand")]
impl StrExt for str {
#[inline]
fn copy_from_str(&mut self, src: &str) {
// SAFETY: The entire slice is being overwritten by `src`, which is a valid `str` reference, and as such is valid UTF-8.
unsafe { self.as_bytes_mut() }.copy_from_slice(src.as_bytes());
}
}