AK: Add a :hex-dump mode to AK::Format

This will just hexdump the given value.
Note that not all formatters respect this, the ones that do are:
- (Readonly)Bytes: formatter added in this commit
- StringView / char const*
- integral types
This commit is contained in:
Ali Mohammad Pur 2021-06-17 13:11:00 +04:30 committed by Ali Mohammad Pur
parent 10f56166e5
commit 7eda164c25
2 changed files with 63 additions and 2 deletions

View file

@ -377,6 +377,29 @@ void FormatBuilder::put_f64(
}
#endif
void FormatBuilder::put_hexdump(ReadonlyBytes bytes, size_t width, char fill)
{
auto put_char_view = [&](auto i) {
put_padding(fill, 4);
for (size_t j = i - width; j < i; ++j) {
auto ch = bytes[j];
m_builder.append(ch >= 32 && ch <= 127 ? ch : '.'); // silly hack
}
};
for (size_t i = 0; i < bytes.size(); ++i) {
if (width > 0) {
if (i % width == 0 && i) {
put_char_view(i);
put_literal("\n");
}
}
put_u64(bytes[i], 16, false, false, true, Align::Right, 2);
}
if (width > 0 && bytes.size() && bytes.size() % width == 0)
put_char_view(bytes.size());
}
void vformat(StringBuilder& builder, StringView fmtstr, TypeErasedFormatParams params)
{
FormatBuilder fmtbuilder { builder };
@ -456,6 +479,8 @@ void StandardFormatter::parse(TypeErasedFormatParams& params, FormatParser& pars
m_mode = Mode::Hexfloat;
else if (parser.consume_specific('A'))
m_mode = Mode::HexfloatUppercase;
else if (parser.consume_specific("hex-dump"))
m_mode = Mode::HexDump;
if (!parser.is_eof())
dbgln("{} did not consume '{}'", __PRETTY_FUNCTION__, parser.remaining());
@ -471,7 +496,7 @@ void Formatter<StringView>::format(FormatBuilder& builder, StringView value)
VERIFY_NOT_REACHED();
if (m_zero_pad)
VERIFY_NOT_REACHED();
if (m_mode != Mode::Default && m_mode != Mode::String && m_mode != Mode::Character)
if (m_mode != Mode::Default && m_mode != Mode::String && m_mode != Mode::Character && m_mode != Mode::HexDump)
VERIFY_NOT_REACHED();
if (m_width.has_value() && m_precision.has_value())
VERIFY_NOT_REACHED();
@ -479,7 +504,10 @@ void Formatter<StringView>::format(FormatBuilder& builder, StringView value)
m_width = m_width.value_or(0);
m_precision = m_precision.value_or(NumericLimits<size_t>::max());
builder.put_string(value, m_align, m_width.value(), m_precision.value(), m_fill);
if (m_mode == Mode::HexDump)
builder.put_hexdump(value.bytes(), m_width.value(), m_fill);
else
builder.put_string(value, m_align, m_width.value(), m_precision.value(), m_fill);
}
void Formatter<FormatString>::vformat(FormatBuilder& builder, StringView fmtstr, TypeErasedFormatParams params)
@ -535,6 +563,10 @@ void Formatter<T, typename EnableIf<IsIntegral<T>>::Type>::format(FormatBuilder&
} else if (m_mode == Mode::HexadecimalUppercase) {
base = 16;
upper_case = true;
} else if (m_mode == Mode::HexDump) {
m_width = m_width.value_or(32);
builder.put_hexdump({ &value, sizeof(value) }, m_width.value(), m_fill);
return;
} else {
VERIFY_NOT_REACHED();
}
@ -563,6 +595,8 @@ void Formatter<bool>::format(FormatBuilder& builder, bool value)
if (m_mode == Mode::Binary || m_mode == Mode::BinaryUppercase || m_mode == Mode::Decimal || m_mode == Mode::Octal || m_mode == Mode::Hexadecimal || m_mode == Mode::HexadecimalUppercase) {
Formatter<u8> formatter { *this };
return formatter.format(builder, static_cast<u8>(value));
} else if (m_mode == Mode::HexDump) {
return builder.put_hexdump({ &value, sizeof(value) }, m_width.value_or(32), m_fill);
} else {
Formatter<StringView> formatter { *this };
return formatter.format(builder, value ? "true" : "false");

View file

@ -196,6 +196,11 @@ public:
SignMode sign_mode = SignMode::OnlyIfNeeded);
#endif
void put_hexdump(
ReadonlyBytes,
size_t width,
char fill = ' ');
const StringBuilder& builder() const
{
return m_builder;
@ -261,6 +266,7 @@ struct StandardFormatter {
Float,
Hexfloat,
HexfloatUppercase,
HexDump,
};
FormatBuilder::Align m_align = FormatBuilder::Align::Default;
@ -296,6 +302,27 @@ struct Formatter<StringView> : StandardFormatter {
void format(FormatBuilder&, StringView value);
};
template<>
struct Formatter<ReadonlyBytes> : Formatter<StringView> {
void format(FormatBuilder& builder, ReadonlyBytes const& value)
{
if (m_mode == Mode::Pointer) {
Formatter<FlatPtr> formatter { *this };
formatter.format(builder, reinterpret_cast<FlatPtr>(value.data()));
} else if (m_mode == Mode::Default || m_mode == Mode::HexDump) {
m_mode = Mode::HexDump;
Formatter<StringView>::format(builder, value);
} else {
Formatter<StringView>::format(builder, value);
}
}
};
template<>
struct Formatter<Bytes> : Formatter<ReadonlyBytes> {
};
template<>
struct Formatter<const char*> : Formatter<StringView> {
void format(FormatBuilder& builder, const char* value)