1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 03:27:34 +00:00

LibCrypto: Make constructing a BigInteger from string fallible

Previously, constructing a `UnsignedBigInteger::from_base()` could
produce an incorrect result if the input string contained a valid
Base36 digit that was out of range of the given base. The same method
would also crash if the input string contained an invalid Base36 digit.
An error is now returned in both these cases.

Constructing a BigFraction from string is now also fallible, so that we
can handle the case where we are given an input string with invalid
digits.
This commit is contained in:
Tim Ledbetter 2024-01-12 21:34:23 +00:00 committed by Andrew Kaster
parent 0b0c7693e2
commit 48a3a02238
11 changed files with 68 additions and 57 deletions

View file

@ -25,20 +25,18 @@ BigFraction::BigFraction(SignedBigInteger value)
{
}
BigFraction::BigFraction(StringView sv)
ErrorOr<BigFraction> BigFraction::from_string(StringView sv)
{
// FIXME: This constructor is definitely fallible, errors should also be propagated
// from both signed and unsigned version of from_base.
auto maybe_dot_index = sv.find('.');
auto integer_part_view = sv.substring_view(0, maybe_dot_index.value_or(sv.length()));
auto fraction_part_view = maybe_dot_index.has_value() ? sv.substring_view(1 + *maybe_dot_index) : "0"sv;
auto integer_part = SignedBigInteger::from_base(10, integer_part_view);
auto fractional_part = SignedBigInteger::from_base(10, fraction_part_view);
auto integer_part = TRY(SignedBigInteger::from_base(10, integer_part_view));
auto fractional_part = TRY(SignedBigInteger::from_base(10, fraction_part_view));
auto fraction_length = UnsignedBigInteger(static_cast<u64>(fraction_part_view.length()));
*this = BigFraction(move(integer_part)) + BigFraction(move(fractional_part), NumberTheory::Power("10"_bigint, move(fraction_length)));
return BigFraction(move(integer_part)) + BigFraction(move(fractional_part), NumberTheory::Power("10"_bigint, move(fraction_length)));
}
BigFraction BigFraction::operator+(BigFraction const& rhs) const

View file

@ -14,7 +14,7 @@ namespace Crypto {
class BigFraction {
// FIXME Make the whole API more error-friendly. This includes:
// - Propagating errors from BigIntegers
// - Returns errors from both BigFraction(StringView) and BigFraction(numerator, denominator);
// - Returns errors from BigFraction(numerator, denominator);
// - Duplicate fallible operators with a error-friendly version
public:
@ -27,9 +27,10 @@ public:
BigFraction& operator=(Crypto::BigFraction const&) = default;
BigFraction& operator=(Crypto::BigFraction&&) = default;
explicit BigFraction(StringView);
explicit BigFraction(double);
static ErrorOr<BigFraction> from_string(StringView);
BigFraction operator+(BigFraction const&) const;
BigFraction operator-(BigFraction const&) const;
BigFraction operator*(BigFraction const&) const;

View file

@ -35,7 +35,7 @@ size_t SignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) cons
return m_unsigned_data.export_data(bytes_view, remove_leading_zeros) + 1;
}
SignedBigInteger SignedBigInteger::from_base(u16 N, StringView str)
ErrorOr<SignedBigInteger> SignedBigInteger::from_base(u16 N, StringView str)
{
auto sign = false;
if (str.length() > 1) {
@ -47,8 +47,8 @@ SignedBigInteger SignedBigInteger::from_base(u16 N, StringView str)
if (maybe_sign == '+')
str = str.substring_view(1);
}
auto unsigned_data = UnsignedBigInteger::from_base(N, str);
return { move(unsigned_data), sign };
auto unsigned_data = TRY(UnsignedBigInteger::from_base(N, str));
return SignedBigInteger { move(unsigned_data), sign };
}
ErrorOr<String> SignedBigInteger::to_base(u16 N) const

View file

@ -63,7 +63,7 @@ public:
size_t export_data(Bytes, bool remove_leading_zeros = false) const;
[[nodiscard]] static SignedBigInteger from_base(u16 N, StringView str);
[[nodiscard]] static ErrorOr<SignedBigInteger> from_base(u16 N, StringView str);
[[nodiscard]] ErrorOr<String> to_base(u16 N) const;
[[nodiscard]] ByteString to_base_deprecated(u16 N) const;
@ -171,5 +171,5 @@ struct AK::Formatter<Crypto::SignedBigInteger> : AK::Formatter<Crypto::UnsignedB
inline Crypto::SignedBigInteger
operator""_sbigint(char const* string, size_t length)
{
return Crypto::SignedBigInteger::from_base(10, { string, length });
return MUST(Crypto::SignedBigInteger::from_base(10, { string, length }));
}

View file

@ -132,16 +132,22 @@ size_t UnsignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) co
return out;
}
UnsignedBigInteger UnsignedBigInteger::from_base(u16 N, StringView str)
ErrorOr<UnsignedBigInteger> UnsignedBigInteger::from_base(u16 N, StringView str)
{
VERIFY(N <= 36);
UnsignedBigInteger result;
UnsignedBigInteger base { N };
for (auto& c : str) {
for (auto const& c : str) {
if (c == '_')
continue;
result = result.multiplied_by(base).plus(parse_ascii_base36_digit(c));
if (!is_ascii_base36_digit(c))
return Error::from_string_literal("Invalid Base36 digit");
auto digit = parse_ascii_base36_digit(c);
if (digit >= N)
return Error::from_string_literal("Base36 digit out of range");
result = result.multiplied_by(base).plus(digit);
}
return result;
}

View file

@ -63,7 +63,7 @@ public:
size_t export_data(Bytes, bool remove_leading_zeros = false) const;
[[nodiscard]] static UnsignedBigInteger from_base(u16 N, StringView str);
[[nodiscard]] static ErrorOr<UnsignedBigInteger> from_base(u16 N, StringView str);
[[nodiscard]] ErrorOr<String> to_base(u16 N) const;
[[nodiscard]] ByteString to_base_deprecated(u16 N) const;
@ -161,5 +161,5 @@ struct AK::Formatter<Crypto::UnsignedBigInteger> : Formatter<StringView> {
inline Crypto::UnsignedBigInteger
operator""_bigint(char const* string, size_t length)
{
return Crypto::UnsignedBigInteger::from_base(10, { string, length });
return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length }));
}