AK: Make Utf16String::number(Integral) fast like String::number()

Move the fast String::number() implementation to a shareable place
so both string types can make use of it.
This commit is contained in:
Andreas Kling
2025-10-04 00:06:38 +02:00
committed by Andreas Kling
parent feafaf09fc
commit 1c04b6da3b
Notes: github-actions[bot] 2025-10-05 09:26:20 +00:00
4 changed files with 79 additions and 39 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2018-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
@@ -14,6 +14,7 @@
#include <AK/MemMem.h>
#include <AK/Stream.h>
#include <AK/String.h>
#include <AK/StringNumber.h>
#include <AK/Utf16View.h>
#include <AK/Vector.h>
#include <stdlib.h>
@@ -563,42 +564,7 @@ String String::roman_number_from(size_t value, Case target_case)
template<Integral T>
String String::number(T value)
{
// Maximum number of base-10 digits for T + sign
constexpr size_t max_digits = sizeof(T) * 3 + 2;
char buffer[max_digits];
char* ptr = buffer + max_digits;
bool is_negative = false;
using UnsignedT = MakeUnsigned<T>;
UnsignedT unsigned_value;
if constexpr (IsSigned<T>) {
if (value < 0) {
is_negative = true;
// Handle signed min correctly
unsigned_value = static_cast<UnsignedT>(0) - static_cast<UnsignedT>(value);
} else {
unsigned_value = static_cast<UnsignedT>(value);
}
} else {
unsigned_value = value;
}
if (unsigned_value == 0) {
*--ptr = '0';
} else {
while (unsigned_value != 0) {
*--ptr = '0' + (unsigned_value % 10);
unsigned_value /= 10;
}
}
if (is_negative) {
*--ptr = '-';
}
size_t size = buffer + max_digits - ptr;
return from_utf8_without_validation(ReadonlyBytes { ptr, size });
return create_string_from_number<String, T>(value);
}
template String String::number(char);

52
AK/StringNumber.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace AK {
template<class StringType, Integral T>
StringType create_string_from_number(T value)
{
// Maximum number of base-10 digits for T + sign
constexpr size_t max_digits = sizeof(T) * 3 + 2;
char buffer[max_digits];
char* ptr = buffer + max_digits;
bool is_negative = false;
using UnsignedT = MakeUnsigned<T>;
UnsignedT unsigned_value;
if constexpr (IsSigned<T>) {
if (value < 0) {
is_negative = true;
// Handle signed min correctly
unsigned_value = static_cast<UnsignedT>(0) - static_cast<UnsignedT>(value);
} else {
unsigned_value = static_cast<UnsignedT>(value);
}
} else {
unsigned_value = value;
}
if (unsigned_value == 0) {
*--ptr = '0';
} else {
while (unsigned_value != 0) {
*--ptr = '0' + (unsigned_value % 10);
unsigned_value /= 10;
}
}
if (is_negative) {
*--ptr = '-';
}
size_t size = buffer + max_digits - ptr;
return StringType::from_utf8_without_validation(ReadonlyBytes { ptr, size });
}
}

View File

@@ -5,6 +5,7 @@
*/
#include <AK/Stream.h>
#include <AK/StringNumber.h>
#include <AK/Utf16String.h>
#include <AK/Utf32View.h>
@@ -166,4 +167,22 @@ ErrorOr<void> Formatter<Utf16String>::format(FormatBuilder& builder, Utf16String
return builder.put_string(utf16_string.ascii_view());
}
template<Integral T>
Utf16String Utf16String::number(T value)
{
return create_string_from_number<Utf16String, T>(value);
}
template Utf16String Utf16String::number(char);
template Utf16String Utf16String::number(signed char);
template Utf16String Utf16String::number(unsigned char);
template Utf16String Utf16String::number(signed short);
template Utf16String Utf16String::number(unsigned short);
template Utf16String Utf16String::number(int);
template Utf16String Utf16String::number(unsigned int);
template Utf16String Utf16String::number(long);
template Utf16String Utf16String::number(unsigned long);
template Utf16String Utf16String::number(long long);
template Utf16String Utf16String::number(unsigned long long);
}

View File

@@ -96,8 +96,11 @@ public:
return builder.to_utf16_string();
}
template<Arithmetic T>
ALWAYS_INLINE static Utf16String number(T value)
template<Integral T>
[[nodiscard]] static Utf16String number(T);
template<FloatingPoint T>
[[nodiscard]] static Utf16String number(T value)
{
return formatted("{}", value);
}