瀏覽代碼

LibCrypto: Added BigInteger 'division by u16' operator

DexesTTP 5 年之前
父節點
當前提交
8aeccf4f02

+ 60 - 6
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<char>(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<char>(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,

+ 2 - 0
Libraries/LibCrypto/BigInt/UnsignedBigInteger.h

@@ -65,6 +65,7 @@ public:
     const AK::Vector<u32, STARTING_WORD_SIZE>& 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;

+ 5 - 7
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;