1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 01:07:44 +00:00

AK: Make string-to-number conversion helpers return Optional

Get rid of the weird old signature:

- int StringType::to_int(bool& ok) const

And replace it with sensible new signature:

- Optional<int> StringType::to_int() const
This commit is contained in:
Andreas Kling 2020-06-12 21:07:52 +02:00
parent 15f4043a7a
commit fdfda6dec2
55 changed files with 354 additions and 455 deletions

View file

@ -26,6 +26,7 @@
#include <AK/FlyString.h>
#include <AK/HashTable.h>
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringUtils.h>
#include <AK/StringView.h>
@ -88,9 +89,9 @@ FlyString::FlyString(const char* string)
{
}
int FlyString::to_int(bool& ok) const
Optional<int> FlyString::to_int() const
{
return StringUtils::convert_to_int(view(), ok);
return StringUtils::convert_to_int(view());
}
bool FlyString::equals_ignoring_case(const StringView& other) const

View file

@ -82,7 +82,7 @@ public:
FlyString to_lowercase() const;
int to_int(bool& ok) const;
Optional<int> to_int() const;
bool equals_ignoring_case(const StringView&) const;
bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;

View file

@ -78,18 +78,11 @@ public:
auto parts = string.split_view('.');
if (parts.size() != 4)
return {};
bool ok;
auto a = parts[0].to_uint(ok);
if (!ok || a > 255)
return {};
auto b = parts[1].to_uint(ok);
if (!ok || b > 255)
return {};
auto c = parts[2].to_uint(ok);
if (!ok || c > 255)
return {};
auto d = parts[3].to_uint(ok);
if (!ok || d > 255)
auto a = parts[0].to_uint().value_or(256);
auto b = parts[1].to_uint().value_or(256);
auto c = parts[2].to_uint().value_or(256);
auto d = parts[3].to_uint().value_or(256);
if (a > 255 || b > 255 || c > 255 || d > 255)
return {};
return IPv4Address((u8)a, (u8)b, (u8)c, (u8)d);
}

View file

@ -123,10 +123,9 @@ String JsonParser::consume_quoted_string()
sb.append(consume());
sb.append(consume());
bool ok;
u32 codepoint = AK::StringUtils::convert_to_uint_from_hex(sb.to_string(), ok);
if (ok && codepoint < 128) {
buffer.append((char)codepoint);
auto codepoint = AK::StringUtils::convert_to_uint_from_hex(sb.to_string());
if (codepoint.has_value() && codepoint.value() < 0x80) {
buffer.append((char)codepoint.value());
} else {
// FIXME: This is obviously not correct, but we don't have non-ASCII support so meh.
buffer.append('?');
@ -202,7 +201,6 @@ JsonValue JsonParser::parse_string()
JsonValue JsonParser::parse_number()
{
bool ok;
JsonValue value;
Vector<char, 128> number_buffer;
Vector<char, 128> fraction_buffer;
@ -231,14 +229,17 @@ JsonValue JsonParser::parse_number()
#ifndef KERNEL
if (is_double) {
int whole = number_string.to_uint(ok);
if (!ok)
whole = number_string.to_int(ok);
ASSERT(ok);
// FIXME: This logic looks shaky.
int whole = 0;
auto to_signed_result = number_string.to_uint();
if (to_signed_result.has_value()) {
whole = to_signed_result.value();
} else {
whole = number_string.to_int().value();
}
int fraction = fraction_string.to_uint(ok);
int fraction = fraction_string.to_uint().value();
fraction *= (whole < 0) ? -1 : 1;
ASSERT(ok);
auto divider = 1;
for (size_t i = 0; i < fraction_buffer.size(); ++i) {
@ -247,10 +248,12 @@ JsonValue JsonParser::parse_number()
value = JsonValue((double)whole + ((double)fraction / divider));
} else {
#endif
value = JsonValue(number_string.to_uint(ok));
if (!ok)
value = JsonValue(number_string.to_int(ok));
ASSERT(ok);
auto to_unsigned_result = number_string.to_uint();
if (to_unsigned_result.has_value()) {
value = JsonValue(to_unsigned_result.value());
} else {
value = JsonValue(number_string.to_int().value());
}
#ifndef KERNEL
}
#endif

View file

@ -196,14 +196,14 @@ ByteBuffer String::to_byte_buffer() const
return ByteBuffer::copy(reinterpret_cast<const u8*>(characters()), length());
}
int String::to_int(bool& ok) const
Optional<int> String::to_int() const
{
return StringUtils::convert_to_int(this->view(), ok);
return StringUtils::convert_to_int(view());
}
unsigned String::to_uint(bool& ok) const
Optional<unsigned> String::to_uint() const
{
return StringUtils::convert_to_uint(this->view(), ok);
return StringUtils::convert_to_uint(view());
}
String String::number(unsigned long long value)

View file

@ -108,8 +108,8 @@ public:
static String repeated(char, size_t count);
bool matches(const StringView& mask, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
int to_int(bool& ok) const;
unsigned to_uint(bool& ok) const;
Optional<int> to_int() const;
Optional<unsigned> to_uint() const;
String to_lowercase() const;
String to_uppercase() const;

View file

@ -26,6 +26,7 @@
*/
#include <AK/Memory.h>
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringUtils.h>
#include <AK/StringView.h>
@ -87,69 +88,54 @@ bool matches(const StringView& str, const StringView& mask, CaseSensitivity case
return (mask_ptr == mask_end) && string_ptr == string_end;
}
int convert_to_int(const StringView& str, bool& ok)
Optional<int> convert_to_int(const StringView& str)
{
if (str.is_empty()) {
ok = false;
return 0;
}
if (str.is_empty())
return {};
bool negative = false;
size_t i = 0;
const auto characters = str.characters_without_null_termination();
if (characters[0] == '-' || characters[0] == '+') {
if (str.length() == 1) {
ok = false;
return 0;
}
if (str.length() == 1)
return {};
i++;
negative = (characters[0] == '-');
}
int value = 0;
for (; i < str.length(); i++) {
if (characters[i] < '0' || characters[i] > '9') {
ok = false;
return 0;
}
if (characters[i] < '0' || characters[i] > '9')
return {};
value = value * 10;
value += characters[i] - '0';
}
ok = true;
return negative ? -value : value;
}
unsigned convert_to_uint(const StringView& str, bool& ok)
Optional<unsigned> convert_to_uint(const StringView& str)
{
if (str.is_empty()) {
ok = false;
return 0;
}
if (str.is_empty())
return {};
unsigned value = 0;
const auto characters = str.characters_without_null_termination();
for (size_t i = 0; i < str.length(); i++) {
if (characters[i] < '0' || characters[i] > '9') {
ok = false;
return 0;
}
if (characters[i] < '0' || characters[i] > '9')
return {};
value = value * 10;
value += characters[i] - '0';
}
ok = true;
return value;
}
unsigned convert_to_uint_from_hex(const StringView& str, bool& ok)
Optional<unsigned> convert_to_uint_from_hex(const StringView& str)
{
if (str.is_empty()) {
ok = false;
return 0;
}
if (str.is_empty())
return {};
unsigned value = 0;
const auto count = str.length();
@ -165,14 +151,11 @@ unsigned convert_to_uint_from_hex(const StringView& str, bool& ok)
} else if (digit >= 'A' && digit <= 'F') {
digit_val = 10 + (digit - 'A');
} else {
ok = false;
return 0;
return {};
}
value = (value << 4) + digit_val;
}
ok = true;
return value;
}

View file

@ -39,9 +39,9 @@ enum class CaseSensitivity {
namespace StringUtils {
bool matches(const StringView& str, const StringView& mask, CaseSensitivity = CaseSensitivity::CaseInsensitive);
int convert_to_int(const StringView&, bool& ok);
unsigned convert_to_uint(const StringView&, bool& ok);
unsigned convert_to_uint_from_hex(const StringView&, bool& ok);
Optional<int> convert_to_int(const StringView&);
Optional<unsigned> convert_to_uint(const StringView&);
Optional<unsigned> convert_to_uint_from_hex(const StringView&);
bool equals_ignoring_case(const StringView&, const StringView&);
bool ends_with(const StringView& a, const StringView& b, CaseSensitivity);
}

View file

@ -215,14 +215,14 @@ StringView StringView::substring_view_starting_after_substring(const StringView&
return { remaining_characters, remaining_length };
}
int StringView::to_int(bool& ok) const
Optional<int> StringView::to_int() const
{
return StringUtils::convert_to_int(*this, ok);
return StringUtils::convert_to_int(*this);
}
unsigned StringView::to_uint(bool& ok) const
Optional<unsigned> StringView::to_uint() const
{
return StringUtils::convert_to_uint(*this, ok);
return StringUtils::convert_to_uint(*this);
}
unsigned StringView::hash() const

View file

@ -96,8 +96,8 @@ public:
// following newline.".
Vector<StringView> lines(bool consider_cr = true) const;
int to_int(bool& ok) const;
unsigned to_uint(bool& ok) const;
Optional<int> to_int() const;
Optional<unsigned> to_uint() const;
// Create a new substring view of this string view, starting either at the beginning of
// the given substring view, or after its end, and continuing until the end of this string

View file

@ -72,8 +72,7 @@ TEST_CASE(order)
}
for (int i = 0; i < 10000; ++i) {
bool ok;
EXPECT_EQ(strings.dequeue().to_int(ok), i);
EXPECT_EQ(strings.dequeue().to_int().value(), i);
}
EXPECT(strings.is_empty());

View file

@ -127,9 +127,8 @@ TEST_CASE(repeated)
TEST_CASE(to_int)
{
bool ok;
EXPECT(String("123").to_int(ok) == 123 && ok);
EXPECT(String("-123").to_int(ok) == -123 && ok);
EXPECT_EQ(String("123").to_int().value(), 123);
EXPECT_EQ(String("-123").to_int().value(), -123);
}
TEST_CASE(to_lowercase)

View file

@ -69,79 +69,88 @@ TEST_CASE(matches_case_insensitive)
TEST_CASE(convert_to_int)
{
bool ok = false;
AK::StringUtils::convert_to_int(StringView(), ok);
EXPECT(!ok);
auto value = AK::StringUtils::convert_to_int(StringView());
EXPECT(!value.has_value());
AK::StringUtils::convert_to_int("", ok);
EXPECT(!ok);
AK::StringUtils::convert_to_int("");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_int("a", ok);
EXPECT(!ok);
AK::StringUtils::convert_to_int("a");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_int("+", ok);
EXPECT(!ok);
AK::StringUtils::convert_to_int("+");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_int("-", ok);
EXPECT(!ok);
AK::StringUtils::convert_to_int("-");
EXPECT(!value.has_value());
int actual = AK::StringUtils::convert_to_int("0", ok);
EXPECT(ok && actual == 0);
auto actual = AK::StringUtils::convert_to_int("0");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 0);
actual = AK::StringUtils::convert_to_int("1", ok);
EXPECT(ok && actual == 1);
actual = AK::StringUtils::convert_to_int("1");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 1);
actual = AK::StringUtils::convert_to_int("+1", ok);
EXPECT(ok && actual == 1);
actual = AK::StringUtils::convert_to_int("+1");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 1);
actual = AK::StringUtils::convert_to_int("-1", ok);
EXPECT(ok && actual == -1);
actual = AK::StringUtils::convert_to_int("-1");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), -1);
actual = AK::StringUtils::convert_to_int("01", ok);
EXPECT(ok && actual == 1);
actual = AK::StringUtils::convert_to_int("01");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 1);
actual = AK::StringUtils::convert_to_int("12345", ok);
EXPECT(ok && actual == 12345);
actual = AK::StringUtils::convert_to_int("12345");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 12345);
actual = AK::StringUtils::convert_to_int("-12345", ok);
EXPECT(ok && actual == -12345);
actual = AK::StringUtils::convert_to_int("-12345");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), -12345);
}
TEST_CASE(convert_to_uint)
{
bool ok = false;
AK::StringUtils::convert_to_uint(StringView(), ok);
EXPECT(!ok);
auto value = AK::StringUtils::convert_to_uint(StringView());
EXPECT(!value.has_value());
AK::StringUtils::convert_to_uint("", ok);
EXPECT(!ok);
value = AK::StringUtils::convert_to_uint("");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_uint("a", ok);
EXPECT(!ok);
value = AK::StringUtils::convert_to_uint("a");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_uint("+", ok);
EXPECT(!ok);
value = AK::StringUtils::convert_to_uint("+");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_uint("-", ok);
EXPECT(!ok);
value = AK::StringUtils::convert_to_uint("-");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_uint("+1", ok);
EXPECT(!ok);
value = AK::StringUtils::convert_to_uint("+1");
EXPECT(!value.has_value());
AK::StringUtils::convert_to_uint("-1", ok);
EXPECT(!ok);
AK::StringUtils::convert_to_uint("-1");
EXPECT(!value.has_value());
unsigned actual = AK::StringUtils::convert_to_uint("0", ok);
EXPECT(ok && actual == 0u);
auto actual = AK::StringUtils::convert_to_uint("0");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 0u);
actual = AK::StringUtils::convert_to_uint("1", ok);
EXPECT(ok && actual == 1u);
actual = AK::StringUtils::convert_to_uint("1");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 1u);
actual = AK::StringUtils::convert_to_uint("01", ok);
EXPECT(ok && actual == 1u);
actual = AK::StringUtils::convert_to_uint("01");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 1u);
actual = AK::StringUtils::convert_to_uint("12345", ok);
EXPECT(ok && actual == 12345u);
actual = AK::StringUtils::convert_to_uint("12345");
EXPECT_EQ(actual.has_value(), true);
EXPECT_EQ(actual.value(), 12345u);
}
TEST_CASE(ends_with)

View file

@ -152,11 +152,11 @@ bool URL::parse(const StringView& string)
if (buffer.is_empty())
return false;
{
bool ok;
m_port = String::copy(buffer).to_uint(ok);
auto port_opt = String::copy(buffer).to_uint();
buffer.clear();
if (!ok)
if (!port_opt.has_value())
return false;
m_port = port_opt.value();
}
if (peek() == '/') {
state = State::InPath;

View file

@ -24,6 +24,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringUtils.h>
@ -60,9 +61,8 @@ String urldecode(const StringView& input)
builder.append(consume());
continue;
}
bool ok;
u8 byte_point = StringUtils::convert_to_uint_from_hex(input.substring_view(cursor + 1, 2), ok);
builder.append(byte_point);
auto byte_point = StringUtils::convert_to_uint_from_hex(input.substring_view(cursor + 1, 2));
builder.append(byte_point.value());
consume();
consume();
consume();