mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 07:47:35 +00:00
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:
parent
10f56166e5
commit
7eda164c25
2 changed files with 63 additions and 2 deletions
|
@ -377,6 +377,29 @@ void FormatBuilder::put_f64(
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
void vformat(StringBuilder& builder, StringView fmtstr, TypeErasedFormatParams params)
|
||||||
{
|
{
|
||||||
FormatBuilder fmtbuilder { builder };
|
FormatBuilder fmtbuilder { builder };
|
||||||
|
@ -456,6 +479,8 @@ void StandardFormatter::parse(TypeErasedFormatParams& params, FormatParser& pars
|
||||||
m_mode = Mode::Hexfloat;
|
m_mode = Mode::Hexfloat;
|
||||||
else if (parser.consume_specific('A'))
|
else if (parser.consume_specific('A'))
|
||||||
m_mode = Mode::HexfloatUppercase;
|
m_mode = Mode::HexfloatUppercase;
|
||||||
|
else if (parser.consume_specific("hex-dump"))
|
||||||
|
m_mode = Mode::HexDump;
|
||||||
|
|
||||||
if (!parser.is_eof())
|
if (!parser.is_eof())
|
||||||
dbgln("{} did not consume '{}'", __PRETTY_FUNCTION__, parser.remaining());
|
dbgln("{} did not consume '{}'", __PRETTY_FUNCTION__, parser.remaining());
|
||||||
|
@ -471,7 +496,7 @@ void Formatter<StringView>::format(FormatBuilder& builder, StringView value)
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
if (m_zero_pad)
|
if (m_zero_pad)
|
||||||
VERIFY_NOT_REACHED();
|
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();
|
VERIFY_NOT_REACHED();
|
||||||
if (m_width.has_value() && m_precision.has_value())
|
if (m_width.has_value() && m_precision.has_value())
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
|
@ -479,7 +504,10 @@ void Formatter<StringView>::format(FormatBuilder& builder, StringView value)
|
||||||
m_width = m_width.value_or(0);
|
m_width = m_width.value_or(0);
|
||||||
m_precision = m_precision.value_or(NumericLimits<size_t>::max());
|
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)
|
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) {
|
} else if (m_mode == Mode::HexadecimalUppercase) {
|
||||||
base = 16;
|
base = 16;
|
||||||
upper_case = true;
|
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 {
|
} else {
|
||||||
VERIFY_NOT_REACHED();
|
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) {
|
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 };
|
Formatter<u8> formatter { *this };
|
||||||
return formatter.format(builder, static_cast<u8>(value));
|
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 {
|
} else {
|
||||||
Formatter<StringView> formatter { *this };
|
Formatter<StringView> formatter { *this };
|
||||||
return formatter.format(builder, value ? "true" : "false");
|
return formatter.format(builder, value ? "true" : "false");
|
||||||
|
|
27
AK/Format.h
27
AK/Format.h
|
@ -196,6 +196,11 @@ public:
|
||||||
SignMode sign_mode = SignMode::OnlyIfNeeded);
|
SignMode sign_mode = SignMode::OnlyIfNeeded);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void put_hexdump(
|
||||||
|
ReadonlyBytes,
|
||||||
|
size_t width,
|
||||||
|
char fill = ' ');
|
||||||
|
|
||||||
const StringBuilder& builder() const
|
const StringBuilder& builder() const
|
||||||
{
|
{
|
||||||
return m_builder;
|
return m_builder;
|
||||||
|
@ -261,6 +266,7 @@ struct StandardFormatter {
|
||||||
Float,
|
Float,
|
||||||
Hexfloat,
|
Hexfloat,
|
||||||
HexfloatUppercase,
|
HexfloatUppercase,
|
||||||
|
HexDump,
|
||||||
};
|
};
|
||||||
|
|
||||||
FormatBuilder::Align m_align = FormatBuilder::Align::Default;
|
FormatBuilder::Align m_align = FormatBuilder::Align::Default;
|
||||||
|
@ -296,6 +302,27 @@ struct Formatter<StringView> : StandardFormatter {
|
||||||
|
|
||||||
void format(FormatBuilder&, StringView value);
|
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<>
|
template<>
|
||||||
struct Formatter<const char*> : Formatter<StringView> {
|
struct Formatter<const char*> : Formatter<StringView> {
|
||||||
void format(FormatBuilder& builder, const char* value)
|
void format(FormatBuilder& builder, const char* value)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue