From 86851fe70c0ff7ff1da98a82edabeef9c2ad989e Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Tue, 18 Oct 2022 21:18:23 +0200 Subject: Draft of iterative_conversion. --- src/passwordmaker/base_conversion/division.rs | 32 -------- .../base_conversion/iterative_conversion.rs | 9 ++- .../base_conversion/iterative_conversion_impl.rs | 87 ++++++++++++++++------ src/passwordmaker/base_conversion/mod.rs | 51 +++++++------ src/passwordmaker/base_conversion/remainders.rs | 43 ----------- .../base_conversion/remainders_impl.rs | 76 ------------------- src/passwordmaker/mod.rs | 56 +++++++------- 7 files changed, 129 insertions(+), 225 deletions(-) delete mode 100644 src/passwordmaker/base_conversion/division.rs delete mode 100644 src/passwordmaker/base_conversion/remainders.rs delete mode 100644 src/passwordmaker/base_conversion/remainders_impl.rs diff --git a/src/passwordmaker/base_conversion/division.rs b/src/passwordmaker/base_conversion/division.rs deleted file mode 100644 index c6fc911..0000000 --- a/src/passwordmaker/base_conversion/division.rs +++ /dev/null @@ -1,32 +0,0 @@ -/// A trait that combines std::ops::Div and std::ops::Rem, as those can often be computed together. -pub(super) trait Division where Self:Sized { - /// does in-place arbitrary-length division. Returns remainder. - fn divide(self, divisor : &D) -> DivisionResult; - fn is_zero(&self) -> bool; -} - -/// Or mark your type as `UseGenericDivision` to just use `/` and `%` operators for types. Makes only sense for integers. -pub(super) trait UseGenericDivision : Clone - + for <'a> std::ops::Div<&'a Self, Output = Self> - + for <'a> std::ops::Rem<&'a Self, Output = Self> - + Default - + Eq {} - - impl Division for U - where U: UseGenericDivision -{ - fn divide(self, divisor : &Self) -> DivisionResult { - DivisionResult { - result: self.clone().div(divisor), - remainder: self.rem(divisor) - } - } - fn is_zero(&self) -> bool { - *self == Self::default() - } -} - -pub(super) struct DivisionResult { - pub result : T, - pub remainder : U, -} \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/iterative_conversion.rs b/src/passwordmaker/base_conversion/iterative_conversion.rs index 94d28c0..9b55141 100644 --- a/src/passwordmaker/base_conversion/iterative_conversion.rs +++ b/src/passwordmaker/base_conversion/iterative_conversion.rs @@ -12,7 +12,7 @@ use std::convert::TryInto; use std::ops::{Mul, DivAssign}; use std::iter::successors; -pub(super) struct IterativeBaseConversion{ +pub(crate) struct IterativeBaseConversion{ current_value : V, current_base_potency : V, remaining_digits : usize, @@ -141,4 +141,11 @@ mod iterative_conversion_tests{ assert_eq!(i.len(), 32); assert_eq!(i.skip_while(|x| *x == 0_u64).collect::>(), vec![7, 5, 0xA, 0x10, 0xC, 0xC, 3, 0xD, 3, 0xA, 3,8,4,8,3]); } + #[test] + fn test_simple_u128_to_base_39_conversion(){ + let i = IterativeBaseConversion::new(MyU128(1234567890123456789u128), 39u64); + assert_eq!(i.len(), 25); + // 3YPRS4FaC1KU + assert_eq!(i.skip_while(|x| *x == 0_u64).collect::>(), vec![3, 34, 25, 27, 28, 4, 15, 36, 12, 1, 20, 30]); + } } \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/iterative_conversion_impl.rs b/src/passwordmaker/base_conversion/iterative_conversion_impl.rs index be1d851..3dceb9b 100644 --- a/src/passwordmaker/base_conversion/iterative_conversion_impl.rs +++ b/src/passwordmaker/base_conversion/iterative_conversion_impl.rs @@ -15,7 +15,13 @@ use std::{ops::{DivAssign, Mul, SubAssign}, convert::{TryFrom, TryInto}, fmt::Di use super::iterative_conversion::RemAssignWithQuotient; //Type to be used as V, with usize as B. -struct SixteenBytes(u128); +pub(crate) struct SixteenBytes(u128); + +impl SixteenBytes{ + pub(super) fn new(value : u128) -> Self { + SixteenBytes(value) + } +} //just for convenience impl From for SixteenBytes{ @@ -58,7 +64,7 @@ impl Mul<&usize> for &SixteenBytes{ //We cannot directly implement all the Foreign traits on arrays directly. So, newtypes again. #[derive(PartialEq, PartialOrd, Ord, Eq, Clone)] -struct ArbitraryBytes([u32;N]); +pub(crate) struct ArbitraryBytes([u32;N]); //Const generics are still a bit limited -> let's just implement From for the exact types we need. impl From<&usize> for ArbitraryBytes<5>{ @@ -116,7 +122,7 @@ impl From<&u32> for ArbitraryBytes<8>{ } //workaround for lack of proper const-generic support. -trait PadWithAZero{ +pub(super) trait PadWithAZero{ type Output; fn pad_with_a_zero(&self) -> Self::Output; } @@ -160,7 +166,7 @@ impl DivAssign<&usize> for ArbitraryBytes{ } #[derive(Debug, Clone, Copy)] -struct ArbitraryBytesToUsizeError; +pub(crate) struct ArbitraryBytesToUsizeError; impl Display for ArbitraryBytesToUsizeError{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "conversion from arbitrary sized int-array to usize failed") @@ -203,7 +209,7 @@ impl TryFrom<&ArbitraryBytes> for usize{ } #[derive(Debug, Clone, Copy)] -struct ArbitraryBytesToU32Error; +pub(crate) struct ArbitraryBytesToU32Error; impl Display for ArbitraryBytesToU32Error{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "conversion from arbitrary sized int-array to u32 failed") @@ -233,7 +239,7 @@ impl Mul<&usize> for &ArbitraryBytes{ //somewhere we need this clone, can just as well be in here... let mut result = self.0.clone(); let carry = result.iter_mut().rev().fold(UsizeAndFour::default(), |carry, digit|{ - assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry always has to fit in usize, otherwise something is terribly wrong. + debug_assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry always has to fit in usize, otherwise something is terribly wrong. let res = (*digit as UsizeAndFour) * (*rhs as UsizeAndFour) + carry; *digit = res as u32; res >> 32 @@ -276,7 +282,7 @@ impl Mul for ArbitraryBytes{ fn mul(mut self, rhs: u32) -> Self::Output { //somewhere we need this clone, can just as well be in here... let carry = self.0.iter_mut().rev().fold(u64::default(), |carry, digit|{ - assert_eq!(carry, carry & (u32::MAX as u64)); //carry always has to fit in usize, otherwise something is terribly wrong. + debug_assert_eq!(carry, carry & (u32::MAX as u64)); //carry always has to fit in usize, otherwise something is terribly wrong. let res = (*digit as u64) * (rhs as u64) + carry; *digit = res as u32; res >> 32 @@ -301,31 +307,35 @@ impl SubAssign<&ArbitraryBytes> for ArbitraryBytes{ 1 } }); - assert_eq!(carry,0); + debug_assert_eq!(carry,0); } } impl ArbitraryBytes{ + pub(super) fn new(data : [u32;N]) -> Self { + ArbitraryBytes(data) + } + /// Replaces self with Quotient and returns Remainder fn div_assign_with_remainder_usize(&mut self, rhs: &usize) -> usize { #[cfg(target_pointer_width = "64")] type UsizeAndFour = u128; #[cfg(not(target_pointer_width = "64"))] type UsizeAndFour = u64; - assert!((UsizeAndFour::MAX >> 32) as u128 >= usize::MAX as u128); + debug_assert!((UsizeAndFour::MAX >> 32) as u128 >= usize::MAX as u128); let divisor : UsizeAndFour = *rhs as UsizeAndFour; let remainder = self.0.iter_mut().fold(0 as UsizeAndFour,|carry, current| { - assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry has to be lower than divisor, and divisor is usize. + debug_assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry has to be lower than divisor, and divisor is usize. let carry_shifted = carry << 32; let dividend = (carry_shifted) + (*current as UsizeAndFour); let ratio = dividend / divisor; - assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. + debug_assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. *current = (ratio) as u32; dividend - (*current as UsizeAndFour) * divisor }); - assert_eq!(remainder, remainder & (usize::MAX as UsizeAndFour)); + debug_assert_eq!(remainder, remainder & (usize::MAX as UsizeAndFour)); remainder as usize } @@ -333,15 +343,15 @@ impl ArbitraryBytes{ fn div_assign_with_remainder_u32(&mut self, rhs: &u32) -> u32 { let divisor : u64 = *rhs as u64; let remainder = self.0.iter_mut().fold(0 as u64,|carry, current| { - assert_eq!(carry, carry & (u32::MAX as u64)); //carry has to be lower than divisor, and divisor is usize. + debug_assert_eq!(carry, carry & (u32::MAX as u64)); //carry has to be lower than divisor, and divisor is usize. let carry_shifted = carry << 32; let dividend = (carry_shifted) + (*current as u64); let ratio = dividend / divisor; - assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. + debug_assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. *current = (ratio) as u32; dividend - (*current as u64) * divisor }); - assert_eq!(remainder, remainder & (u32::MAX as u64)); + debug_assert_eq!(remainder, remainder & (u32::MAX as u64)); remainder as u32 } @@ -355,10 +365,10 @@ impl ArbitraryBytes{ where Self : PadWithAZero> + for<'a> From<&'a usize> { - assert!(M == N+1); + debug_assert!(M == N+1); //first we need to find n (number of digits in divisor) let n_digits_divisor= N - divisor.find_first_nonzero_digit(); - assert!(n_digits_divisor > 1); + debug_assert!(n_digits_divisor > 1); //and same in the non-normalized dividend let m_plus_n_digits_dividend = N - self.find_first_nonzero_digit(); let m_extra_digits_dividend = m_plus_n_digits_dividend - n_digits_divisor; @@ -376,7 +386,7 @@ impl ArbitraryBytes{ let divisor_second_significant_digit = divisor.get_digit_from_right(n_digits_divisor-2) as u64; //step D2, D7: the loop. - for j in m_extra_digits_dividend..=0 { + for j in (0..=m_extra_digits_dividend).rev() { //Step D3: Guess a digit let guess_dividend = ((dividend.get_digit_from_right(j+n_digits_divisor) as u64)<<32) + (dividend.get_digit_from_right(j + n_digits_divisor - 1) as u64); let mut guesstimate = guess_dividend/guess_divisor; @@ -393,7 +403,7 @@ impl ArbitraryBytes{ //I'm too tired to do this by the book. If this thing is gonna blow, we can just as well increase our guesstimate by one and call it a day. //In any case, this does only happen in _very_ rare cases. Soo: //Steps D4-D6. - assert!(guesstimate & (u32::MAX as u64) == guesstimate); //Knuth says this is a one-place number, and I trust him. + debug_assert!(guesstimate & (u32::MAX as u64) == guesstimate); //Knuth says this is a one-place number, and I trust him. let mut guesstimate = guesstimate as u32; let mut s = (divisor.clone() * guesstimate).expect("Multipliation by a digit cannot overflow for a padded type."); let will_overflow = @@ -402,7 +412,7 @@ impl ArbitraryBytes{ if will_overflow { guesstimate -= 1; s -= &divisor; - assert!(std::cmp::Ord::cmp(÷nd.0[(M - 1 - (j+n_digits_divisor))..=(M - 1 - j)], &s.0[(M - 1 - n_digits_divisor)..=(M - 1)]) != Ordering::Less) + debug_assert!(std::cmp::Ord::cmp(÷nd.0[(M - 1 - (j+n_digits_divisor))..=(M - 1 - j)], &s.0[(M - 1 - n_digits_divisor)..=(M - 1)]) != Ordering::Less) } slice_sub_assign(&mut dividend.0[(M - 1 - (j+n_digits_divisor))..=(M - 1 - j)], &s.0[(M - 1 - n_digits_divisor)..=(M - 1)]); quotient.set_digit_from_right(guesstimate, j); @@ -428,7 +438,7 @@ impl ArbitraryBytes{ } fn slice_sub_assign(lhs : &mut [u32], rhs: &[u32]){ - assert_eq!(lhs.len(), rhs.len()); + debug_assert_eq!(lhs.len(), rhs.len()); let carry = lhs.iter_mut().zip(rhs.iter()).rev().fold(0_u64,|carry,(i,s)| { let s = (*s as u64) + carry; if *i as u64 >= s { @@ -439,5 +449,38 @@ fn slice_sub_assign(lhs : &mut [u32], rhs: &[u32]){ 1 } }); - assert_eq!(carry,0); + debug_assert_eq!(carry,0); +} + +#[cfg(test)] +mod iterative_conversion_impl_tests{ + use super::*; + + #[test] + fn knuth_add_back_test(){ + let mut dividend = ArbitraryBytes::new([ + //m = 3, n=5 + u32::MAX, + u32::MAX, + u32::MAX-1, + u32::MAX, + u32::MAX, + 0, + 0, + 3 + ]); + let divisor = ArbitraryBytes::new([ + 0, + 0, + 0, + 0, + 0, + u32::MAX, + u32::MAX, + u32::MAX, + ]); + let result = dividend.rem_assign_with_quotient(&divisor); + assert_eq!(dividend.0, [0,0,0,0,0,0,0,2]); + assert_eq!(result.0, [0,0,0,u32::MAX,u32::MAX, u32::MAX, u32::MAX, u32::MAX]); + } } \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/mod.rs b/src/passwordmaker/base_conversion/mod.rs index 4ae7653..b1ffedf 100644 --- a/src/passwordmaker/base_conversion/mod.rs +++ b/src/passwordmaker/base_conversion/mod.rs @@ -1,60 +1,63 @@ use std::convert::TryInto; +use iterative_conversion_impl::PadWithAZero; +pub(super) use iterative_conversion::IterativeBaseConversion; +pub(super) use iterative_conversion_impl::{SixteenBytes, ArbitraryBytes}; -use remainders::CalcRemainders; - -mod division; mod iterative_conversion; mod iterative_conversion_impl; -mod remainders; -mod remainders_impl; - /// Converts an input to a different base (which fits in usize). Returns the digits starting at the most significant one. pub(super) trait BaseConversion { + type Output : ExactSizeIterator; // return type is subject to change. Hopefully soon the math will be rewritten, so we can skip the Vec and IntoIter. // will have to remain an ExactSizeIterator though. - fn convert_to_base(self, base : usize) -> std::iter::Rev>; + fn convert_to_base(self, base : usize) -> Self::Output; } -impl BaseConversion for T where T : ToI32Array{ - fn convert_to_base(self, base : usize) -> std::iter::Rev> { - self.to_int_array().calc_remainders(base).collect::>().into_iter().rev() +impl BaseConversion for T + where T : ToArbitraryBytes>, + for<'a> T::Output: From<&'a usize> + From<&'a u32> + PadWithAZero>, +{ + type Output = IterativeBaseConversion, usize>; + fn convert_to_base(self, base : usize) -> Self::Output { + IterativeBaseConversion::new(self.to_arbitrary_bytes(), base) } } impl BaseConversion for [u8;16]{ - fn convert_to_base(self, base : usize) -> std::iter::Rev> { - u128::from_be_bytes(self).calc_remainders(base as u128).map(|ll| ll as usize).collect::>().into_iter().rev() + type Output = IterativeBaseConversion; + fn convert_to_base(self, base : usize) -> IterativeBaseConversion { + IterativeBaseConversion::new(SixteenBytes::new(u128::from_be_bytes(self)), base) } } // Rust 1.52 only has a very limited support for const generics. This means, we'll have to live with this not-too-constrained solution... -pub(super) trait ToI32Array { +pub(super) trait ToArbitraryBytes { type Output; - fn to_int_array(self) -> Self::Output; + fn to_arbitrary_bytes(self) -> Self::Output; } //this could of course be done in a generic manner, but it's ugly without array_mut, which we don't have in Rust 1.52. //Soo, pedestrian's approach :D -impl ToI32Array for [u8;20] { - type Output = [u32; 5]; - fn to_int_array(self) -> [u32; 5] { - [ +impl ToArbitraryBytes for [u8;20] { + type Output = ArbitraryBytes<5>; + fn to_arbitrary_bytes(self) -> ArbitraryBytes<5> { + ArbitraryBytes::new([ u32::from_be_bytes(self[0..4].try_into().unwrap()), u32::from_be_bytes(self[4..8].try_into().unwrap()), u32::from_be_bytes(self[8..12].try_into().unwrap()), u32::from_be_bytes(self[12..16].try_into().unwrap()), u32::from_be_bytes(self[16..20].try_into().unwrap()), - ] + ]) } } -impl ToI32Array for [u8;32] { - type Output = [u32; 8]; - fn to_int_array(self) -> [u32; 8] { - [ +impl ToArbitraryBytes for [u8;32] { + type Output = ArbitraryBytes<8>; + fn to_arbitrary_bytes(self) -> ArbitraryBytes<8> { + ArbitraryBytes::new([ u32::from_be_bytes(self[0..4].try_into().unwrap()), u32::from_be_bytes(self[4..8].try_into().unwrap()), u32::from_be_bytes(self[8..12].try_into().unwrap()), @@ -63,6 +66,6 @@ impl ToI32Array for [u8;32] { u32::from_be_bytes(self[20..24].try_into().unwrap()), u32::from_be_bytes(self[24..28].try_into().unwrap()), u32::from_be_bytes(self[28..32].try_into().unwrap()), - ] + ]) } } \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/remainders.rs b/src/passwordmaker/base_conversion/remainders.rs deleted file mode 100644 index 344fab2..0000000 --- a/src/passwordmaker/base_conversion/remainders.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::division::{Division, DivisionResult}; - -/// Trait used for the old base conversion. -pub(super) trait CalcRemainders{ - fn calc_remainders(self, divisor : D) -> Remainders; -} - -impl CalcRemainders for T - where T:Division -{ - fn calc_remainders(self, divisor : D) -> Remainders { - Remainders::new(self,divisor) - } -} - -pub(super) struct Remainders{ - value : Option, - divisor : U, -} - -impl> Remainders { - fn new(value : T, divisor : U) -> Self { - let value = if value.is_zero() { None } else { Some(value) }; - Remainders { - value, - divisor, - } - } -} - -impl> Iterator for Remainders{ - type Item=U; - - fn next(&mut self) -> Option { - if let Some(v) = self.value.take() { - let DivisionResult{result, remainder} = v.divide(&self.divisor); - self.value = if result.is_zero() { None } else { Some(result) }; - Some(remainder) - } else { - None - } - } -} \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/remainders_impl.rs b/src/passwordmaker/base_conversion/remainders_impl.rs deleted file mode 100644 index c2431bb..0000000 --- a/src/passwordmaker/base_conversion/remainders_impl.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::division::{Division, UseGenericDivision, DivisionResult}; - -impl UseGenericDivision for u128{} //for Md4, Md5 - -impl Division for [u32;N] { - #[allow(clippy::cast_possible_truncation)] - fn divide(mut self, divisor : &usize) -> DivisionResult { - #[cfg(target_pointer_width = "64")] - type UsizeAndFour = u128; - #[cfg(not(target_pointer_width = "64"))] - type UsizeAndFour = u64; - assert!((UsizeAndFour::MAX >> 32) as u128 >= usize::MAX as u128); - - //uses mutation, because why not? self is owned after all :D - let divisor : UsizeAndFour = *divisor as UsizeAndFour; - let remainder = self.iter_mut().fold(0 as UsizeAndFour,|carry, current| { - assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry has to be lower than divisor, and divisor is usize. - let carry_shifted = carry << 32; - let dividend = (carry_shifted) + (*current as UsizeAndFour); - let ratio = dividend / divisor; - assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. - *current = (ratio) as u32; - dividend - (*current as UsizeAndFour) * divisor - }); - assert_eq!(remainder, remainder & (usize::MAX as UsizeAndFour)); - let remainder = remainder as usize; - DivisionResult{ - result: self, - remainder, - } - } - - fn is_zero(&self) -> bool { - self.iter().all(|x| *x == 0) - } -} - -#[cfg(test)] -mod remainders_tests{ - use super::super::remainders::CalcRemainders; - - use super::*; - #[test] - fn test_generic_division(){ - let v = 50u128; - let d = 7u128; - let DivisionResult{result, remainder}=v.divide(&d); - assert_eq!(7, result); - assert_eq!(1, remainder); - } - - #[test] - fn test_remainders() { - //relies on generic division. - let v = 141u128; - let d = 3u128; - let results : Vec = v.calc_remainders(d).collect(); - assert_eq!(results, vec![0u128,2u128,0u128,2u128,1u128]) - } - - #[test] - fn test_array_divide() { - let dividend_int = 0xe7f1ec3a5f35af805407a8a531eefb79u128; - let dividend = [(dividend_int >> 96) as u32, ((dividend_int >> 64) & 0xffffffff) as u32, ((dividend_int >> 32) & 0xffffffff) as u32, (dividend_int & 0xffffffff) as u32]; - #[cfg(target_pointer_width = "64")] - let divisor = 1531534813576487; - #[cfg(not(target_pointer_width = "64"))] - let divisor = 1531534813; - let result_int = dividend_int / (divisor as u128); - let remainder_int = dividend_int % (divisor as u128); - let result = dividend.divide(&divisor); - assert_eq!(result.result, [(result_int >> 96) as u32, ((result_int >> 64) & 0xffffffff) as u32, ((result_int >> 32) & 0xffffffff) as u32, (result_int & 0xffffffff) as u32]); - assert_eq!(remainder_int, result.remainder as u128); - } - -} \ No newline at end of file diff --git a/src/passwordmaker/mod.rs b/src/passwordmaker/mod.rs index a96698d..5e51ee9 100644 --- a/src/passwordmaker/mod.rs +++ b/src/passwordmaker/mod.rs @@ -1,10 +1,13 @@ -use std::iter::repeat; +use std::iter::SkipWhile; + use unicode_segmentation::UnicodeSegmentation; use leet::LeetReplacementTable; use grapheme::Grapheme; use base_conversion::BaseConversion; +use self::base_conversion::{IterativeBaseConversion, SixteenBytes, ArbitraryBytes}; + use super::Hasher; mod base_conversion; @@ -94,7 +97,6 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let message = yeet_upper_bytes(&message).collect::>(); let hash = H::MD5::hash(&message); let grapheme_indices = hash.convert_to_base(characters.len()); - let grapheme_indices = yoink_additional_graphemes_for_06_if_needed(grapheme_indices); GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::V06(grapheme_indices)} } @@ -112,7 +114,6 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let data = yeet_upper_bytes(data); let hash = hmac::hmac::(key, data); let grapheme_indices = hash.convert_to_base(characters.len()); - let grapheme_indices = yoink_additional_graphemes_for_06_if_needed(grapheme_indices); GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::V06(grapheme_indices)} } @@ -128,17 +129,17 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let data = leetified_data.as_deref().unwrap_or(data); let grapheme_indices = match algo { Algorithm::Md4 => - modern_hmac_to_grapheme_indices::(&key, data, characters.len()), + GetGraphemesIteratorInner::Modern16(modern_hmac_to_grapheme_indices::(&key, data, characters.len()).skip_while(is_zero)), Algorithm::Md5 => - modern_hmac_to_grapheme_indices::(&key, data, characters.len()), + GetGraphemesIteratorInner::Modern16(modern_hmac_to_grapheme_indices::(&key, data, characters.len()).skip_while(is_zero)), Algorithm::Sha1 => - modern_hmac_to_grapheme_indices::(&key, data, characters.len()), + GetGraphemesIteratorInner::Modern20(modern_hmac_to_grapheme_indices::(&key, data, characters.len()).skip_while(is_zero)), Algorithm::Sha256 => - modern_hmac_to_grapheme_indices::(&key, data, characters.len()), + GetGraphemesIteratorInner::Modern32(modern_hmac_to_grapheme_indices::(&key, data, characters.len()).skip_while(is_zero)), Algorithm::Ripemd160 => - modern_hmac_to_grapheme_indices::(&key, data, characters.len()), + GetGraphemesIteratorInner::Modern20(modern_hmac_to_grapheme_indices::(&key, data, characters.len()).skip_while(is_zero)), }; - GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::Modern(grapheme_indices)} + GetGraphemesIterator { graphemes : characters, inner: grapheme_indices} } fn generate_password_part_modern<'a>( @@ -152,17 +153,17 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let message = pre_leet_level.as_ref().map(|l| l.leetify(&message)).unwrap_or(message); let grapheme_indices = match algo { Algorithm::Md4 => - modern_message_to_grapheme_indices::(&message, characters.len()), + GetGraphemesIteratorInner::Modern16(modern_message_to_grapheme_indices::(&message, characters.len()).skip_while(is_zero)), Algorithm::Md5 => - modern_message_to_grapheme_indices::(&message,characters.len()), + GetGraphemesIteratorInner::Modern16(modern_message_to_grapheme_indices::(&message,characters.len()).skip_while(is_zero)), Algorithm::Sha1 => - modern_message_to_grapheme_indices::(&message,characters.len()), + GetGraphemesIteratorInner::Modern20(modern_message_to_grapheme_indices::(&message,characters.len()).skip_while(is_zero)), Algorithm::Sha256 => - modern_message_to_grapheme_indices::(&message,characters.len()), + GetGraphemesIteratorInner::Modern32(modern_message_to_grapheme_indices::(&message,characters.len()).skip_while(is_zero)), Algorithm::Ripemd160 => - modern_message_to_grapheme_indices::(&message,characters.len()), + GetGraphemesIteratorInner::Modern20(modern_message_to_grapheme_indices::(&message,characters.len()).skip_while(is_zero)), }; - GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::Modern(grapheme_indices)} + GetGraphemesIterator { graphemes : characters, inner: grapheme_indices} } } @@ -195,9 +196,15 @@ fn combine_prefix_password_suffix<'a, T : Iterator>>(password: .collect() } +fn is_zero(i : &usize) -> bool { + *i == 0 +} + enum GetGraphemesIteratorInner { - Modern(std::iter::Rev>), - V06(std::iter::Chain>, std::iter::Rev>>) + Modern16(SkipWhile,fn(&usize)->bool>), + Modern20(SkipWhile,usize>,fn(&usize)->bool>), + Modern32(SkipWhile,usize>,fn(&usize)->bool>), + V06(IterativeBaseConversion) } struct GetGraphemesIterator<'a> { graphemes : &'a Vec>, @@ -212,21 +219,23 @@ impl<'a> Iterator for GetGraphemesIterator<'a> { fn next(&mut self) -> Option { let idx = match &mut self.inner { - GetGraphemesIteratorInner::Modern(i) => i.next(), + GetGraphemesIteratorInner::Modern16(i) => i.next(), + GetGraphemesIteratorInner::Modern20(i) => i.next(), + GetGraphemesIteratorInner::Modern32(i) => i.next(), GetGraphemesIteratorInner::V06(i) => i.next(), }; idx.and_then(|idx| self.graphemes.get(idx).cloned()) } } -fn modern_hmac_to_grapheme_indices(key : &str, data: &str, divisor : usize) -> std::iter::Rev> +fn modern_hmac_to_grapheme_indices(key : &str, data: &str, divisor : usize) -> <::Output as BaseConversion>::Output where T:Hasher, ::Output: BaseConversion + AsRef<[u8]> { hmac::hmac::(key.bytes(), data.bytes()).convert_to_base(divisor) } -fn modern_message_to_grapheme_indices(data: &str, divisor : usize) -> std::iter::Rev> +fn modern_message_to_grapheme_indices(data: &str, divisor : usize) -> <::Output as BaseConversion>::Output where T:Hasher, ::Output: BaseConversion { @@ -307,11 +316,4 @@ impl AlgoSelection { #[allow(clippy::cast_possible_truncation)] //clippy, stop complaining. Truncating is the very purpose of this function... fn yeet_upper_bytes(input : &str) -> impl Iterator + Clone + '_ { input.encode_utf16().map(|wide_char| wide_char as u8) -} - -//signature subject to change, but need named types... -fn yoink_additional_graphemes_for_06_if_needed(input : std::iter::Rev>) - -> std::iter::Chain>, std::iter::Rev>> -{ - repeat(0_usize).take(32-input.len()).chain(input) } \ No newline at end of file -- cgit v1.2.3