/*
* \brief Utilities for formatted text output
* \author Norman Feske
* \date 2022-03-14
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__OS__UTIL__FORMATTED_OUTPUT_H_
#define _INCLUDE__OS__UTIL__FORMATTED_OUTPUT_H_
#include
#include
namespace Genode {
template struct Truncated;
template struct Repeated;
template struct Left_aligned;
template struct Right_aligned;
class Hex_dump;
/**
* Return the number of characters needed when rendering 'args' as text
*/
template
static unsigned printed_length(ARGS &&... args)
{
struct _Output : Output
{
unsigned count = 0;
void out_char(char) override { count++; }
} _output;
print(_output, args...);
return _output.count;
}
}
template
struct Genode::Truncated
{
unsigned const _limit;
T const &_arg;
Truncated(unsigned limit, T const &arg) : _limit(limit), _arg(arg) { }
void print(Output &out) const
{
struct _Output : Output
{
Output &out;
unsigned const limit;
unsigned count = 0;
void out_char(char c) override
{
if (count++ < limit)
out.out_char(c);
}
_Output(Output &out, unsigned limit) : out(out), limit(limit) { }
} _output(out, _limit);
Genode::print(_output, _arg);
}
};
template
struct Genode::Repeated
{
unsigned const _n;
T const &_arg;
Repeated(unsigned n, T const &arg) : _n(n), _arg(arg) { }
void print(Output &out) const
{
for (unsigned i = 0; i < _n; i++)
Genode::print(out, _arg);
}
};
template
struct Genode::Left_aligned
{
unsigned const _n;
T const &_arg;
Left_aligned(unsigned n, T const &arg) : _n(n), _arg(arg) { }
void print(Output &out) const
{
unsigned const len = min(printed_length(_arg), _n);
Genode::print(out, Truncated(len, _arg), Repeated(_n - len, Char(' ')));
}
};
template
struct Genode::Right_aligned
{
unsigned const _n;
T const &_arg;
Right_aligned(unsigned n, T const &arg) : _n(n), _arg(arg) { }
void print(Output &out) const
{
unsigned const len = min(printed_length(_arg), _n);
Genode::print(out, Repeated(_n - len, Char(' ')), Truncated(len, _arg));
}
};
class Genode::Hex_dump
{
private:
static constexpr size_t _max_bytes_per_line { 16 };
uint8_t const * const _base;
size_t const _size;
size_t const _num_lines { (_size + _max_bytes_per_line - 1) / _max_bytes_per_line };
static bool _printable_ascii(char c)
{
return c > 31 && c < 127;
}
static void _print_line_as_hex_words(Output &out, uint8_t const *base, size_t size)
{
using Genode::print;
static constexpr size_t max_bytes_per_word { 2 };
for (size_t idx { 0 }; idx < _max_bytes_per_line; idx++) {
if (idx && idx % max_bytes_per_word == 0)
print(out, " ");
if (idx < size)
print(out, Hex(base[idx], Hex::OMIT_PREFIX, Hex::PAD));
else
print(out, " ");
}
}
static void _print_line_as_ascii(Output &out, uint8_t const *base, size_t size)
{
using Genode::print;
for (size_t idx { 0 }; idx < size; idx++) {
char const *char_ptr { (char *)&base[idx] };
if (_printable_ascii(*char_ptr))
print(out, Cstring { char_ptr, 1 });
else
print(out, ".");
}
}
static void _print_line_offset(Output &out, size_t line_offset)
{
Genode::print(out, Hex((uint32_t)line_offset, Hex::OMIT_PREFIX, Hex::PAD));
}
public:
Hex_dump(auto const &range)
: _base { (uint8_t *)range.start }, _size { range.num_bytes } { }
void print(Output &out) const
{
using Genode::print;
uint8_t const *prev_line_ptr { nullptr };
bool prev_line_was_duplicate { false };
for (size_t line_idx { 0 }; line_idx < _num_lines; line_idx++) {
size_t const line_offset { line_idx * _max_bytes_per_line };
size_t const line_size { min((size_t)_max_bytes_per_line, _size - line_offset) };
uint8_t const *line_ptr { &_base[line_offset] };
bool const last_line { line_idx == _num_lines - 1 };
bool line_is_duplicate { false };
if (prev_line_ptr)
line_is_duplicate = !memcmp(prev_line_ptr, line_ptr, line_size);
if (!line_is_duplicate || last_line) {
_print_line_offset(out, line_offset);
print(out, ": ");
_print_line_as_hex_words(out, line_ptr, line_size);
print(out, " ");
_print_line_as_ascii(out, line_ptr, line_size);
print(out, last_line ? "" : "\n");
}
if (line_is_duplicate && !prev_line_was_duplicate && !last_line)
print(out, "*\n");
prev_line_ptr = line_ptr;
prev_line_was_duplicate = line_is_duplicate;
}
}
};
#endif /* _INCLUDE__OS__UTIL__FORMATTED_OUTPUT_H_ */