mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:24:57 +00:00
AK+Format: Add new integer to string backend.
I put this into the <AK/PrintfImplementation.h> header in the hope that it could be re-used by the printf implementation. That would not be super trivial though, so I am not doing that now.
This commit is contained in:
parent
8af67210cf
commit
2111fc5f63
2 changed files with 291 additions and 0 deletions
|
@ -29,6 +29,7 @@
|
|||
#include <AK/Assertions.h>
|
||||
#include <AK/LogStream.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
@ -37,6 +38,172 @@ namespace PrintfImplementation {
|
|||
static constexpr const char* printf_hex_digits_lower = "0123456789abcdef";
|
||||
static constexpr const char* printf_hex_digits_upper = "0123456789ABCDEF";
|
||||
|
||||
enum class Align {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
};
|
||||
|
||||
enum class SignMode {
|
||||
OnlyIfNeeded,
|
||||
Always,
|
||||
Reserved
|
||||
};
|
||||
|
||||
// The worst case is that we have the largest 64-bit value formatted as binary number, this would take
|
||||
// 65 bytes. Choosing a larger power of two won't hurt and is a bit of mitigation against out-of-bounds accesses.
|
||||
inline size_t convert_unsigned_to_string(u64 value, Array<u8, 128>& buffer, u8 base, bool upper_case)
|
||||
{
|
||||
ASSERT(base >= 2 && base <= 16);
|
||||
|
||||
static constexpr const char* lowercase_lookup = "0123456789abcdef";
|
||||
static constexpr const char* uppercase_lookup = "0123456789ABCDEF";
|
||||
|
||||
if (value == 0) {
|
||||
buffer[0] = '0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t used = 0;
|
||||
while (value > 0) {
|
||||
if (upper_case)
|
||||
buffer[used++] = uppercase_lookup[value % base];
|
||||
else
|
||||
buffer[used++] = lowercase_lookup[value % base];
|
||||
|
||||
value /= base;
|
||||
}
|
||||
|
||||
// Reverse the list; I came up with this logic in like three seconds so it's probably wrong in some edge case.
|
||||
for (size_t i = 0; i < used / 2; ++i)
|
||||
swap(buffer[i], buffer[used - i - 1]);
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
inline size_t convert_unsigned_to_string(
|
||||
u64 value,
|
||||
StringBuilder& builder,
|
||||
u8 base = 10,
|
||||
bool prefix = false,
|
||||
bool upper_case = false,
|
||||
bool zero_pad = false,
|
||||
Align align = Align::Right,
|
||||
size_t width = 0,
|
||||
char fill = ' ',
|
||||
SignMode sign_mode = SignMode::OnlyIfNeeded,
|
||||
bool is_negative = false)
|
||||
{
|
||||
Array<u8, 128> buffer;
|
||||
|
||||
const auto used_by_significant_digits = convert_unsigned_to_string(value, buffer, base, upper_case);
|
||||
size_t used_by_prefix = sign_mode == SignMode::OnlyIfNeeded ? static_cast<size_t>(is_negative) : 1;
|
||||
|
||||
if (prefix) {
|
||||
if (base == 8)
|
||||
used_by_prefix += 1;
|
||||
else if (base == 16)
|
||||
used_by_prefix += 2;
|
||||
else if (base == 2)
|
||||
used_by_prefix += 2;
|
||||
}
|
||||
|
||||
const auto put_prefix = [&]() {
|
||||
if (is_negative)
|
||||
builder.append('-');
|
||||
else if (sign_mode == SignMode::Always)
|
||||
builder.append('+');
|
||||
else if (sign_mode == SignMode::Reserved)
|
||||
builder.append(' ');
|
||||
|
||||
if (prefix) {
|
||||
if (base == 2) {
|
||||
if (upper_case)
|
||||
builder.append("0B");
|
||||
else
|
||||
builder.append("0b");
|
||||
} else if (base == 8) {
|
||||
builder.append("0");
|
||||
} else if (base == 16) {
|
||||
if (upper_case)
|
||||
builder.append("0X");
|
||||
else
|
||||
builder.append("0x");
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto put_padding = [&](size_t amount, char fill) {
|
||||
for (size_t i = 0; i < amount; ++i)
|
||||
builder.append(fill);
|
||||
};
|
||||
const auto put_digits = [&]() {
|
||||
builder.append(StringView { buffer.span().trim(used_by_significant_digits) });
|
||||
};
|
||||
|
||||
const auto used_by_field = used_by_significant_digits + used_by_prefix;
|
||||
const auto used_by_padding = static_cast<size_t>(max<ssize_t>(0, static_cast<ssize_t>(width) - static_cast<ssize_t>(used_by_field)));
|
||||
|
||||
if (align == Align::Left) {
|
||||
const auto used_by_right_padding = used_by_padding;
|
||||
|
||||
put_prefix();
|
||||
put_digits();
|
||||
put_padding(used_by_right_padding, fill);
|
||||
|
||||
return used_by_field + used_by_right_padding;
|
||||
}
|
||||
|
||||
if (align == Align::Center) {
|
||||
const auto used_by_left_padding = used_by_padding / 2;
|
||||
const auto used_by_right_padding = ceil_div<size_t, size_t>(used_by_padding, 2);
|
||||
|
||||
put_padding(used_by_left_padding, fill);
|
||||
put_prefix();
|
||||
put_digits();
|
||||
put_padding(used_by_right_padding, fill);
|
||||
|
||||
return used_by_left_padding + used_by_field + used_by_right_padding;
|
||||
}
|
||||
|
||||
if (align == Align::Right) {
|
||||
const auto used_by_left_padding = used_by_padding;
|
||||
|
||||
if (zero_pad) {
|
||||
put_prefix();
|
||||
put_padding(used_by_left_padding, '0');
|
||||
put_digits();
|
||||
} else {
|
||||
put_padding(used_by_left_padding, fill);
|
||||
put_prefix();
|
||||
put_digits();
|
||||
}
|
||||
|
||||
return used_by_field + used_by_left_padding;
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
inline size_t convert_signed_to_string(
|
||||
i64 value,
|
||||
StringBuilder& builder,
|
||||
u8 base = 10,
|
||||
bool common_prefix = false,
|
||||
bool upper_case = false,
|
||||
bool zero_pad = false,
|
||||
Align align = Align::Right,
|
||||
size_t width = 0,
|
||||
char fill = ' ',
|
||||
SignMode sign_mode = SignMode::OnlyIfNeeded)
|
||||
{
|
||||
bool is_negative = value < 0;
|
||||
|
||||
if (value < 0)
|
||||
value = -value;
|
||||
|
||||
return convert_unsigned_to_string(static_cast<size_t>(value), builder, base, common_prefix, upper_case, zero_pad, align, width, fill, sign_mode, is_negative);
|
||||
}
|
||||
|
||||
#ifdef __serenity__
|
||||
extern "C" size_t strlen(const char*);
|
||||
#else
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue