diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index d62499f45e6..31b2216d473 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -191,6 +191,41 @@ template Optional convert_to_uint_from_hex(StringView str, TrimWhitespace); template Optional convert_to_uint_from_hex(StringView str, TrimWhitespace); template Optional convert_to_uint_from_hex(StringView str, TrimWhitespace); +template +Optional convert_to_uint_from_octal(StringView str, TrimWhitespace trim_whitespace) +{ + auto string = trim_whitespace == TrimWhitespace::Yes + ? str.trim_whitespace() + : str; + if (string.is_empty()) + return {}; + + T value = 0; + const auto count = string.length(); + const T upper_bound = NumericLimits::max(); + + for (size_t i = 0; i < count; i++) { + char digit = string[i]; + u8 digit_val; + if (value > (upper_bound >> 3)) + return {}; + + if (digit >= '0' && digit <= '7') { + digit_val = digit - '0'; + } else { + return {}; + } + + value = (value << 3) + digit_val; + } + return value; +} + +template Optional convert_to_uint_from_octal(StringView str, TrimWhitespace); +template Optional convert_to_uint_from_octal(StringView str, TrimWhitespace); +template Optional convert_to_uint_from_octal(StringView str, TrimWhitespace); +template Optional convert_to_uint_from_octal(StringView str, TrimWhitespace); + bool equals_ignoring_case(StringView a, StringView b) { if (a.length() != b.length()) diff --git a/AK/StringUtils.h b/AK/StringUtils.h index ca079cb82ad..775e28ac6c6 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -56,6 +56,8 @@ template Optional convert_to_uint(StringView, TrimWhitespace = TrimWhitespace::Yes); template Optional convert_to_uint_from_hex(StringView, TrimWhitespace = TrimWhitespace::Yes); +template +Optional convert_to_uint_from_octal(StringView, TrimWhitespace = TrimWhitespace::Yes); bool equals_ignoring_case(StringView, StringView); bool ends_with(StringView a, StringView b, CaseSensitivity); bool starts_with(StringView, StringView, CaseSensitivity); diff --git a/Tests/AK/TestStringUtils.cpp b/Tests/AK/TestStringUtils.cpp index e634778e2a1..bff9aa5dcdb 100644 --- a/Tests/AK/TestStringUtils.cpp +++ b/Tests/AK/TestStringUtils.cpp @@ -226,6 +226,60 @@ TEST_CASE(convert_to_uint) EXPECT(!actual_u64.has_value()); } +TEST_CASE(convert_to_uint_from_octal) +{ + auto value = AK::StringUtils::convert_to_uint_from_octal(StringView()); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal(""); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal("a"); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal("+"); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal("-"); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal("+1"); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal("-1"); + EXPECT(!value.has_value()); + + value = AK::StringUtils::convert_to_uint_from_octal("8"); + EXPECT(!value.has_value()); + + auto actual = AK::StringUtils::convert_to_uint_from_octal("77777777"); + EXPECT(!actual.has_value()); + + actual = AK::StringUtils::convert_to_uint_from_octal("0"); + EXPECT_EQ(actual.has_value(), true); + EXPECT_EQ(actual.value(), 0u); + + actual = AK::StringUtils::convert_to_uint_from_octal("1"); + EXPECT_EQ(actual.has_value(), true); + EXPECT_EQ(actual.value(), 1u); + + actual = AK::StringUtils::convert_to_uint_from_octal("0755"); + EXPECT_EQ(actual.has_value(), true); + EXPECT_EQ(actual.value(), 0755u); + + actual = AK::StringUtils::convert_to_uint_from_octal("755"); + EXPECT_EQ(actual.has_value(), true); + EXPECT_EQ(actual.value(), 0755u); + + actual = AK::StringUtils::convert_to_uint_from_octal(" \t644 \n\n"); + EXPECT_EQ(actual.has_value(), true); + EXPECT_EQ(actual.value(), 0644u); + + actual = AK::StringUtils::convert_to_uint_from_octal("177777"); + EXPECT_EQ(actual.has_value(), true); + EXPECT_EQ(actual.value(), 0177777u); +} + TEST_CASE(ends_with) { String test_string = "ABCDEF";