diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 2f1e4e5aa2..5e03c71726 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -52,14 +52,17 @@ UnsignedBigInteger UnsignedBigInteger::import_data(const u8* ptr, size_t length) size_t UnsignedBigInteger::export_data(AK::ByteBuffer& data) { UnsignedBigInteger copy { *this }; + UnsignedBigInteger quotient; + UnsignedBigInteger remainder; size_t size = trimmed_length() * sizeof(u32); size_t i = 0; for (; i < size; ++i) { - if (copy.length() == 0) + if (copy.trimmed_length() == 0) break; data[size - i - 1] = copy.m_words[0] & 0xff; - copy = copy.divided_by(256).quotient; + divide_u16_without_allocation(copy, 256, quotient, remainder); + copy.set_to(quotient); } return i; } @@ -79,12 +82,14 @@ String UnsignedBigInteger::to_base10() const { StringBuilder builder; UnsignedBigInteger temp(*this); + UnsignedBigInteger quotient; + UnsignedBigInteger remainder; while (temp != UnsignedBigInteger { 0 }) { - auto div_result = temp.divided_by({ 10 }); - ASSERT(div_result.remainder.words()[0] < 10); - builder.append(static_cast(div_result.remainder.words()[0] + '0')); - temp = div_result.quotient; + divide_u16_without_allocation(temp, 10, quotient, remainder); + ASSERT(remainder.words()[0] < 10); + builder.append(static_cast(remainder.words()[0] + '0')); + temp.set_to(quotient); } auto reversed_string = builder.to_string(); @@ -102,6 +107,13 @@ void UnsignedBigInteger::set_to_0() m_is_invalid = false; } +void UnsignedBigInteger::set_to(u32 other) +{ + m_is_invalid = false; + m_words.clear_with_capacity(); + m_words.append(other); +} + void UnsignedBigInteger::set_to(const UnsignedBigInteger& other) { m_is_invalid = other.m_is_invalid; @@ -168,6 +180,13 @@ FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigI UnsignedBigInteger quotient; UnsignedBigInteger remainder; + // If we actually have a u16-compatible divisor, short-circuit to the + // less computationally-intensive "divide_u16_without_allocation" method. + if (divisor.trimmed_length() == 1 && divisor.m_words[0] < (1 << 16)) { + divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder); + return UnsignedDivisionResult { quotient, remainder }; + } + UnsignedBigInteger temp_shift_result; UnsignedBigInteger temp_shift_plus; UnsignedBigInteger temp_shift; @@ -434,6 +453,41 @@ FLATTEN void UnsignedBigInteger::divide_without_allocation( } } +/** + * Complexity : O(N) where N is the number of digits in the numerator + * Division method : + * Starting from the most significant one, for each half-word of the numerator, combine it + * with the existing remainder if any, divide the combined number as a u32 operation and + * update the quotient / remainder as needed. + */ +FLATTEN void UnsignedBigInteger::divide_u16_without_allocation( + const UnsignedBigInteger& numerator, + u32 denominator, + UnsignedBigInteger& quotient, + UnsignedBigInteger& remainder) +{ + ASSERT(denominator < (1 << 16)); + u32 remainder_word = 0; + auto numerator_length = numerator.trimmed_length(); + quotient.set_to_0(); + quotient.m_words.resize(numerator_length); + for (int word_index = numerator_length - 1; word_index >= 0; --word_index) { + auto word_high = numerator.m_words[word_index] >> 16; + auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1); + + auto number_to_divide_high = (remainder_word << 16) | word_high; + auto quotient_high = number_to_divide_high / denominator; + remainder_word = number_to_divide_high % denominator; + + auto number_to_divide_low = remainder_word << 16 | word_low; + auto quotient_low = number_to_divide_low / denominator; + remainder_word = number_to_divide_low % denominator; + + quotient.m_words[word_index] = (quotient_high << 16) | quotient_low; + } + remainder.set_to(remainder_word); +} + ALWAYS_INLINE void UnsignedBigInteger::shift_left_by_n_words( const UnsignedBigInteger& number, const size_t number_of_words, diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index 72928c5616..f6aa6b4232 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -65,6 +65,7 @@ public: const AK::Vector& words() const { return m_words; } void set_to_0(); + void set_to(u32 other); void set_to(const UnsignedBigInteger& other); void invalidate() { m_is_invalid = true; } @@ -87,6 +88,7 @@ public: static void shift_left_without_allocation(const UnsignedBigInteger& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); static void multiply_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output); static void divide_without_allocation(const UnsignedBigInteger& numerator, const UnsignedBigInteger& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); + static void divide_u16_without_allocation(const UnsignedBigInteger& numerator, u32 denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder); bool operator==(const UnsignedBigInteger& other) const; bool operator!=(const UnsignedBigInteger& other) const; diff --git a/Libraries/LibCrypto/NumberTheory/ModularFunctions.h b/Libraries/LibCrypto/NumberTheory/ModularFunctions.h index 59f0c1d505..46752ac40b 100644 --- a/Libraries/LibCrypto/NumberTheory/ModularFunctions.h +++ b/Libraries/LibCrypto/NumberTheory/ModularFunctions.h @@ -39,7 +39,6 @@ static auto ModularInverse(const UnsignedBigInteger& a_, const UnsignedBigIntege return { 1 }; UnsignedBigInteger one { 1 }; - UnsignedBigInteger two { 2 }; UnsignedBigInteger temp_1; UnsignedBigInteger temp_2; UnsignedBigInteger temp_3; @@ -82,11 +81,11 @@ static auto ModularInverse(const UnsignedBigInteger& a_, const UnsignedBigIntege } // u /= 2 - UnsignedBigInteger::divide_without_allocation(u, two, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + UnsignedBigInteger::divide_u16_without_allocation(u, 2, temp_quotient, temp_remainder); u.set_to(temp_quotient); // d /= 2 - UnsignedBigInteger::divide_without_allocation(d, two, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + UnsignedBigInteger::divide_u16_without_allocation(d, 2, temp_quotient, temp_remainder); d.set_to(temp_quotient); } } @@ -107,11 +106,11 @@ static auto ModularInverse(const UnsignedBigInteger& a_, const UnsignedBigIntege } // v /= 2 - UnsignedBigInteger::divide_without_allocation(v, two, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + UnsignedBigInteger::divide_u16_without_allocation(v, 2, temp_quotient, temp_remainder); v.set_to(temp_quotient); // x /= 2 - UnsignedBigInteger::divide_without_allocation(x, two, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + UnsignedBigInteger::divide_u16_without_allocation(x, 2, temp_quotient, temp_remainder); x.set_to(temp_quotient); } } @@ -129,7 +128,6 @@ static auto ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& UnsignedBigInteger ep { e }; UnsignedBigInteger base { b }; UnsignedBigInteger exp { 1 }; - UnsignedBigInteger two { 2 }; UnsignedBigInteger temp_1; UnsignedBigInteger temp_2; @@ -151,7 +149,7 @@ static auto ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& } // ep = ep / 2; - UnsignedBigInteger::divide_without_allocation(ep, two, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder); + UnsignedBigInteger::divide_u16_without_allocation(ep, 2, temp_quotient, temp_remainder); ep.set_to(temp_quotient); // base = (base * base) % m;