Skip to content

Instantly share code, notes, and snippets.

@jevinskie
Created October 8, 2024 06:32
Show Gist options
  • Save jevinskie/cf3f34569a86bdab8395293f3cebe80b to your computer and use it in GitHub Desktop.
Save jevinskie/cf3f34569a86bdab8395293f3cebe80b to your computer and use it in GitHub Desktop.
{fmt} extended hexdump
#pragma once
#include "common.hpp"
#include "fmt/base.h"
#include <functional>
#include <iterator>
#include <type_traits>
#undef NDEBUG
#include <algorithm>
#include <cassert>
#include <cctype>
#include <concepts>
#include <cstdio>
#include <string_view>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fmt/compile.h>
#include <fmt/format.h>
#include <fmt/printf.h>
#include <fmt/ranges.h>
namespace gsnr {
using namespace fmt::literals;
static constexpr size_t ehexdump_fallback_term_width = 80;
static constexpr size_t ehexdump_min_bytes_per_line = 4;
static constexpr size_t ehexdump_max_bytes_per_line = 256;
static constexpr const std::string_view ehexdump_extascii_table[] = {
"▪"sv, "☺"sv, "☻"sv, "♥"sv, "♦"sv, "♣"sv, "♠"sv, "•"sv, "◘"sv, "○"sv, "◙"sv, "♂"sv, "♀"sv,
"♪"sv, "♫"sv, "☼"sv, "►"sv, "◄"sv, "↕"sv, "‼"sv, "¶"sv, "§"sv, "▬"sv, "↨"sv, "↑"sv, "↓"sv,
"→"sv, "←"sv, "∟"sv, "↔"sv, "▲"sv, "▼"sv, " "sv, "!"sv, "\""sv, "#"sv, "$"sv, "%"sv, "&"sv,
"'"sv, "("sv, ")"sv, "*"sv, "+"sv, ","sv, "-"sv, "."sv, "/"sv, "0"sv, "1"sv, "2"sv, "3"sv,
"4"sv, "5"sv, "6"sv, "7"sv, "8"sv, "9"sv, ":"sv, ";"sv, "<"sv, "="sv, ">"sv, "?"sv, "@"sv,
"A"sv, "B"sv, "C"sv, "D"sv, "E"sv, "F"sv, "G"sv, "H"sv, "I"sv, "J"sv, "K"sv, "L"sv, "M"sv,
"N"sv, "O"sv, "P"sv, "Q"sv, "R"sv, "S"sv, "T"sv, "U"sv, "V"sv, "W"sv, "X"sv, "Y"sv, "Z"sv,
"["sv, "\\"sv, "]"sv, "^"sv, "_"sv, "`"sv, "a"sv, "b"sv, "c"sv, "d"sv, "e"sv, "f"sv, "g"sv,
"h"sv, "i"sv, "j"sv, "k"sv, "l"sv, "m"sv, "n"sv, "o"sv, "p"sv, "q"sv, "r"sv, "s"sv, "t"sv,
"u"sv, "v"sv, "w"sv, "x"sv, "y"sv, "z"sv, "{"sv, "|"sv, "}"sv, "~"sv, "⌂"sv, "█"sv, "⡀"sv,
"⢀"sv, "⣀"sv, "⠠"sv, "⡠"sv, "⢠"sv, "⣠"sv, "⠄"sv, "⡄"sv, "⢄"sv, "⣄"sv, "⠤"sv, "⡤"sv, "⢤"sv,
"⣤"sv, "⠁"sv, "⡁"sv, "⢁"sv, "⣁"sv, "⠡"sv, "⡡"sv, "⢡"sv, "⣡"sv, "⠅"sv, "⡅"sv, "⢅"sv, "⣅"sv,
"⠥"sv, "⡥"sv, "⢥"sv, "⣥"sv, "⠃"sv, "⡃"sv, "⢃"sv, "⣃"sv, "⠣"sv, "⡣"sv, "⢣"sv, "⣣"sv, "⠇"sv,
"⡇"sv, "⢇"sv, "⣇"sv, "⠧"sv, "⡧"sv, "⢧"sv, "⣧"sv, "⠉"sv, "⡉"sv, "⢉"sv, "⣉"sv, "⠩"sv, "⡩"sv,
"⢩"sv, "⣩"sv, "⠍"sv, "⡍"sv, "⢍"sv, "⣍"sv, "⠭"sv, "⡭"sv, "⢭"sv, "⣭"sv, "⠊"sv, "⡊"sv, "⢊"sv,
"⣊"sv, "⠪"sv, "⡪"sv, "⢪"sv, "⣪"sv, "⠎"sv, "⡎"sv, "⢎"sv, "⣎"sv, "⠮"sv, "⡮"sv, "⢮"sv, "⣮"sv,
"⠑"sv, "⡑"sv, "⢑"sv, "⣑"sv, "⠱"sv, "⡱"sv, "⢱"sv, "⣱"sv, "⠕"sv, "⡕"sv, "⢕"sv, "⣕"sv, "⠵"sv,
"⡵"sv, "⢵"sv, "⣵"sv, "⠚"sv, "⡚"sv, "⢚"sv, "⣚"sv, "⠺"sv, "⡺"sv, "⢺"sv, "⣺"sv, "⠞"sv, "⡞"sv,
"⢞"sv, "⣞"sv, "⠾"sv, "⡾"sv, "⢾"sv, "⣾"sv, "⠛"sv, "⡛"sv, "⢛"sv, "⣛"sv, "⠻"sv, "⡻"sv, "⢻"sv,
"⣻"sv, "⠟"sv, "⡟"sv, "⢟"sv, "⣟"sv, "⠿"sv, "⡿"sv, "⢿"sv, "⣿"sv,
};
static_assert(sizeof_array(ehexdump_extascii_table) == 256);
static constexpr size_t ehexdump_extascii_table_max_len = sv_table_max_len(ehexdump_extascii_table);
static constexpr size_t ehexdump_get_terminal_width() {
if consteval {
return ehexdump_fallback_term_width;
}
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
return w.ws_col;
}
return ehexdump_fallback_term_width;
}
static constexpr char classic_hexdump_repr(const uint8_t b) {
if (b >= 0x20 && b < 0x7F) {
return static_cast<char>(b);
}
return '.';
}
#if 1
static constexpr bool ehexdump_debug = true;
#else
static constexpr bool ehexdump_debug = false;
#endif
#if 1
static constexpr bool ehexdump_debug_reserve = true;
#else
static constexpr bool ehexdump_debug_reserve = false;
#endif
GSNR_EXPORT fmt::memory_buffer ehexdump_mb(const void *data, const size_t sz,
const bool extended = true, const bool offset = true,
const bool caps = false, const size_t width = 0) {
constexpr size_t hex_len_per_byte = 3; // 2 hex digits + 1 space
constexpr size_t ascii_len_per_byte = 1; // 1 ASCII character
constexpr auto ascii_max_sz_per_byte = ehexdump_extascii_table_max_len;
constexpr auto pad_between_off_and_hex = " "sv;
constexpr auto pad_between_off_and_hex_sz = pad_between_off_and_hex.size();
constexpr auto pad_between_hex_and_repr = " |"sv;
constexpr auto pad_between_hex_and_repr_sz = pad_between_hex_and_repr.size();
constexpr auto off_pre = "0x"sv;
constexpr auto off_pre_len = off_pre.size();
constexpr auto line_end = "|\n"sv;
constexpr auto line_end_sz = line_end.size();
constexpr auto line_end_sz_no_nl = line_end_sz - 1;
const auto byte_data = static_cast<const uint8_t *>(data);
// Calculate num_off_digits using integer arithmetic
const size_t num_off_digits = offset ? hex_nibbles_needed_even(sz) : (sizeof(void *) * 2);
const size_t calc_len = width == 0 ? ehexdump_get_terminal_width() : width;
// Calculate bytes_per_line using integer arithmetic
const size_t off_len = off_pre_len + num_off_digits;
const size_t non_data_len =
off_len + pad_between_off_and_hex_sz + pad_between_hex_and_repr_sz + line_end_sz_no_nl;
const size_t avail_len = (calc_len > non_data_len) ? (calc_len - non_data_len) : 0;
size_t intermediate_bytes_per_line = avail_len / (hex_len_per_byte + ascii_len_per_byte);
// Ensure a minimum of 4 bytes per line
intermediate_bytes_per_line =
std::max(ehexdump_min_bytes_per_line, intermediate_bytes_per_line);
// Ensure a maximum of 256 bytes per line
intermediate_bytes_per_line =
std::min(ehexdump_max_bytes_per_line, intermediate_bytes_per_line);
const size_t bytes_per_line = intermediate_bytes_per_line;
// Calculate the exact total size to reserve upfront
const size_t num_lines_full = sz / bytes_per_line;
const size_t num_bytes_trailing = sz % bytes_per_line;
const size_t max_full_line_sz = off_len + (bytes_per_line * hex_len_per_byte) +
pad_between_off_and_hex_sz + pad_between_hex_and_repr_sz +
(bytes_per_line * ascii_max_sz_per_byte) + line_end_sz;
const size_t max_trailing_line_sz =
num_bytes_trailing ? (off_len + (bytes_per_line * hex_len_per_byte) +
pad_between_off_and_hex_sz + pad_between_hex_and_repr_sz +
(num_bytes_trailing * ascii_max_sz_per_byte) + line_end_sz)
: 0;
const size_t max_total_sz = num_lines_full * max_full_line_sz + max_trailing_line_sz;
fmt::memory_buffer buf;
#if 1
buf.reserve(max_total_sz);
#else
// add space for NUL terminator for no-copy std::string conversion
// NOTE: std::string and/or C++ is dumb and can't do this
buf.reserve(max_total_sz + 1);
#endif
if (ehexdump_debug) {
fmt::print(
::stderr,
"ehexdump dbg: data: {} sz: {:d} extended: {:s} offset: {:s} caps: {:s} width {:d}\n"
"hex_len_per_byte: {:d} ascii_len_per_byte: {:d} ascii_max_sz_per_byte: {:d}\n"
"pad_between_off_and_hex: \"{:s}\" pad_between_off_and_hex_sz: {:d}\n"
"pad_between_hex_and_repr: \"{:s}\" pad_between_hex_and_repr_sz: {:d}\n"
"off_pre_len: {:d} num_off_digits: {:d} off_len: {:d} line_end_sz: {:d}\n"
"calc_len: {:d} non_data_len: {:d} avail_len: {:d} bytes_per_line: {:d}\n"
"num_lines_full: {:d} num_bytes_trailing: {:d} max_full_line_sz: {:d} "
"max_trailing_line_sz: {:d} max_total_sz: {:d}\n",
fmt::ptr(data), sz, boolmoji(extended), boolmoji(offset), boolmoji(caps), width,
hex_len_per_byte, ascii_len_per_byte, ascii_max_sz_per_byte, pad_between_off_and_hex,
pad_between_off_and_hex_sz, pad_between_hex_and_repr, pad_between_hex_and_repr_sz,
off_pre_len, num_off_digits, off_len, line_end_sz, calc_len, non_data_len, avail_len,
bytes_per_line, num_lines_full, num_bytes_trailing, max_full_line_sz,
max_trailing_line_sz, max_total_sz);
}
for (size_t line_offset = 0; line_offset < sz; line_offset += bytes_per_line) {
const size_t line_bytes = std::min(bytes_per_line, sz - line_offset);
// Offset column
const uintptr_t off =
offset ? line_offset : reinterpret_cast<uintptr_t>(byte_data) + line_offset;
if (caps) {
fmt::format_to(std::back_inserter(buf), "0x{:0{}X} "sv, off, num_off_digits);
} else {
fmt::format_to(std::back_inserter(buf), "0x{:0{}x} "sv, off, num_off_digits);
}
// Hex values column
if (caps) {
fmt::format_to(
std::back_inserter(buf), "{:02X}",
fmt::join(&byte_data[line_offset], &byte_data[line_offset + line_bytes], " "sv));
} else {
fmt::format_to(
std::back_inserter(buf), "{:02x}",
fmt::join(&byte_data[line_offset], &byte_data[line_offset + line_bytes], " "sv));
}
// Padding for missing bytes if any
if (line_bytes < bytes_per_line) {
fmt::format_to(std::back_inserter(buf), "{:{}s}"sv, ""sv,
(bytes_per_line - line_bytes) * hex_len_per_byte);
}
// Padding between hex and ASCII representation columns
buf.append(pad_between_hex_and_repr);
// (extended) ASCII representation column
if (extended) {
for (size_t i = 0; i < line_bytes; ++i) {
buf.append(ehexdump_extascii_table[byte_data[line_offset + i]]);
}
} else {
for (size_t i = 0; i < line_bytes; ++i) {
buf.push_back(classic_hexdump_repr(byte_data[line_offset + i]));
}
}
buf.append(line_end);
}
if (ehexdump_debug_reserve) {
fmt::print(::stderr, "max_total_sz: {:d} buf capacity: {:d} size: {:d}\n", max_total_sz,
buf.capacity(), buf.size());
}
return buf;
}
template <typename T>
GSNR_EXPORT fmt::memory_buffer ehexdump_mb(const std::span<T> &span, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
return ehexdump_mb(static_cast<const void *>(span.data()), span.size_bytes(), extended, offset,
caps, width);
}
template <typename T, size_t N>
GSNR_EXPORT fmt::memory_buffer ehexdump_mb(const std::span<T, N> &span, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
return ehexdump_mb(static_cast<const void *>(span.data()), span.size_bytes(), extended, offset,
caps, width);
}
template <typename T>
requires(!std::is_pointer_v<T>)
GSNR_EXPORT fmt::memory_buffer ehexdump_mb(const T &data, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
return ehexdump_mb(static_cast<const void *>(&data), sizeof(data), extended, offset, caps,
width);
}
GSNR_EXPORT std::string ehexdump(const void *data, const size_t sz, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
auto mb = ehexdump_mb(data, sz, extended, offset, caps, width);
const auto mb_sz = mb.size();
assume(mb_sz < std::string{}.max_size());
// elide SSO codegen
assume(mb_sz > sizeof(std::string{}));
return {mb.data(), mb_sz};
}
template <typename T>
GSNR_EXPORT std::string ehexdump(const std::span<T> &span, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
auto mb = ehexdump_mb(span, extended, offset, caps, width);
const auto mb_sz = mb.size();
assume(mb_sz < std::string{}.max_size());
// elide SSO codegen
assume(mb_sz > sizeof(std::string{}));
return {mb.data(), mb_sz};
}
template <typename T, size_t N>
GSNR_EXPORT std::string ehexdump(const std::span<T, N> &span, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
auto mb = ehexdump_mb(span, extended, offset, caps, width);
const auto mb_sz = mb.size();
assume(mb_sz < std::string{}.max_size());
// elide SSO codegen
assume(mb_sz > sizeof(std::string{}));
return {mb.data(), mb_sz};
}
template <typename T>
requires(!std::is_pointer_v<T>)
GSNR_EXPORT std::string ehexdump(const T &data, const bool extended = true,
const bool offset = true, const bool caps = false,
const size_t width = 0) {
auto mb = ehexdump_mb(data, extended, offset, caps, width);
const auto mb_sz = mb.size();
assume(mb_sz < std::string{}.max_size());
// elide SSO codegen
assume(mb_sz > sizeof(std::string{}));
return {mb.data(), mb_sz};
}
} // namespace gsnr
namespace fmt {
// Struct to hold formatting options for the ehexdump formatter
struct ehexdump_specs {
uint32_t data_ = 0;
enum : uint32_t {
width_mask = 0x000000FFu, // First 8 bits for width (4-256 - 4)
precision_mask = 0x07FFFF00u, // Next 19 bits for precision (0-524287)
not_offset_flag = 0x08000000u, // Bit 27 for not_offset
uppercase_flag = 0x10000000u, // Bit 28 for uppercase
not_extended_flag = 0x20000000u, // Bit 29 for non-extended Unicode form (`#`)
dynamic_width_flag = 0x40000000u, // Bit 30 for dynamic width
dynamic_precision_flag = 0x80000000u, // Bit 31 for dynamic precision
};
static constexpr uint32_t width_min = 4;
static constexpr uint32_t width_max = 256;
static constexpr uint32_t precision_max = 524287;
static constexpr uint32_t width_shift = 0;
static constexpr uint32_t precision_shift = 8;
constexpr void set_width(const uint32_t w) {
fmt::print("specs::set_width({})\n", w);
if (w < width_min) {
throw format_error("Width < 4 in format string");
} else if (w > width_max) {
throw format_error("Width > 256 in format string");
} else {
data_ = (data_ & ~width_mask) |
(((w - width_min + 1) & (width_mask >> width_shift)) << width_shift);
}
}
constexpr uint32_t width() const {
const auto w = ((data_ & width_mask) >> width_shift) + (width_min - 1);
return w < width_min ? 0 : w;
}
constexpr bool set_precision(const size_t p) {
fmt::print("specs::set_precision({})\n", p);
if (p > precision_max) {
fmt::print("p ({}) > precision_max ({}), setting dynamic_precision_flag\n", p,
precision_max);
set_dynamic_precision(true); // Mark precision as dynamic if it's too large
return true;
} else {
data_ = (data_ & ~precision_mask) |
((p & (precision_mask >> precision_shift)) << precision_shift);
}
return false;
}
constexpr size_t precision() const {
return (data_ & precision_mask) >> precision_shift;
}
constexpr void set_not_offset(const bool not_offset) {
fmt::print("specs::set_not_offset({})\n", not_offset);
if (not_offset) {
data_ |= not_offset_flag;
} else {
data_ &= ~not_offset_flag;
}
}
constexpr bool is_not_offset() const {
return (data_ & not_offset_flag) != 0;
}
constexpr void set_uppercase(const bool uppercase) {
fmt::print("specs::set_uppercase({})\n", uppercase);
if (uppercase) {
data_ |= uppercase_flag;
} else {
data_ &= ~uppercase_flag;
}
}
constexpr bool is_uppercase() const {
return (data_ & uppercase_flag) != 0;
}
constexpr void set_not_extended(const bool not_extended) {
fmt::print("specs::set_not_extended({})\n", not_extended);
if (not_extended) {
data_ |= not_extended_flag;
} else {
data_ &= ~not_extended_flag;
}
}
constexpr bool is_not_extended() const {
return (data_ & not_extended_flag) != 0;
}
constexpr void set_dynamic_width(const bool dynamic_width) {
fmt::print("specs::set_dynamic_width({})\n", dynamic_width);
if (dynamic_width) {
data_ |= dynamic_width_flag;
} else {
data_ &= ~dynamic_width_flag;
}
}
constexpr bool is_dynamic_width() const {
return (data_ & dynamic_width_flag) != 0;
}
constexpr void set_dynamic_precision(const bool dynamic_precision) {
fmt::print("specs::set_dynamic_precision({})\n", dynamic_precision);
if (dynamic_precision) {
data_ |= dynamic_precision_flag;
} else {
data_ &= ~dynamic_precision_flag;
}
}
constexpr bool is_dynamic_precision() const {
return (data_ & dynamic_precision_flag) != 0;
}
};
// Base formatter to handle common parsing logic
struct ehexdump_formatter_base {
ehexdump_specs specs;
uint32_t dynamic_width = 0;
size_t dynamic_precision = std::numeric_limits<size_t>::max();
int dynamic_width_arg_id = -1;
int dynamic_precision_arg_id = -1;
bool custom_ehexdump = false;
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
const auto end = ctx.end();
fmt::print("ehexdump_formatter_base::parse() begin of \"{}\"\n", std::string_view{it, end});
// Parse extended flag
if (it != end && *it == '#') {
specs.set_not_extended(true);
++it;
}
// Parse not_offset flag
if (it != end && *it == '0') {
specs.set_not_offset(true);
++it;
}
if (it != end) {
if (*it >= '0' && *it <= '9') {
// Parse static width
uint32_t w = 0;
while (it != end && (*it >= '0' && *it <= '9')) {
w = w * 10 + (*it - '0');
++it;
}
specs.set_width(w);
} else if (*it == '{') {
// Parse dynamic width
dynamic_width_arg_id = ctx.next_arg_id();
it = std::find(it, end, '}');
++it;
fmt::print("setting dynamic_width_arg_id: {}\n", dynamic_width_arg_id);
specs.set_dynamic_width(true);
}
}
if (it != end) {
if (*it == '.') {
// Parse precision
++it;
if (it == end) {
throw format_error("Expected precision after '.'");
}
if (*it == '{') {
// Handle dynamic precision
dynamic_precision_arg_id = ctx.next_arg_id();
it = std::find(it, end, '}');
++it;
fmt::print("setting dynamic_precision_arg_id: {}\n", dynamic_precision_arg_id);
specs.set_dynamic_precision(true);
} else if (*it >= '0' && *it <= '9') {
fmt::print("static precision parse start\n");
size_t p = 0;
while (it != end && (*it >= '0' && *it <= '9')) {
p = p * 10 + (*it - '0');
++it;
}
if (p == 0) {
throw format_error("Expected non-zero precision");
}
fmt::print("setting precision: {}\n", p);
if (specs.set_precision(p)) {
// static precision too large to fit in specs
dynamic_precision = p;
}
} else {
throw format_error("Expected precision after '.'");
}
}
}
if (it != end) {
// Handle type specifier (h or H)
if (*it == 'h') {
custom_ehexdump = true;
++it;
} else if (*it == 'H') {
custom_ehexdump = true;
specs.set_uppercase(true);
++it;
}
}
fmt::print("ehexdump_formatter_base::parse() end of \"{}\"\n", std::string_view{it, end});
// FIXME: if !custom_ehexdump return begin?
return it;
};
};
struct ehexdump_formatter_base_stub {
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
} // namespace fmt
namespace gsnr {
// An argument visitor that returns true iff arg is a zero integer.
struct int_visitor {
template <typename T, FMT_ENABLE_IF(std::is_integral_v<T>)> auto operator()(T value) -> int {
return value;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral_v<T>)> auto operator()(T) -> int {
return -1;
}
};
template <typename T> struct generic_ehexdump_formatter : fmt::ehexdump_formatter_base {
constexpr auto format(const T &f, fmt::format_context &ctx) const -> decltype(ctx.out()) {
fmt::print("sizeof(T): {} specs.data_: {:#08x} custom_ehexdump: {} dynamic_precision: {} "
"dynamic_precision_arg_id: {} dynamic_width: {} dynamic_width_arg_id: {} "
"precision(): {} width(): {} is_uppercase(): {} is_not_extended(): {} "
"is_not_offset(): {} is_dynamic_precision(): {} is_dynamic_width(): {}\n",
sizeof(T), specs.data_, custom_ehexdump, dynamic_precision,
dynamic_precision_arg_id, dynamic_width, dynamic_width_arg_id, specs.precision(),
specs.width(), specs.is_uppercase(), specs.is_not_extended(),
specs.is_not_offset(), specs.is_dynamic_precision(), specs.is_dynamic_width());
if (custom_ehexdump) {
const auto d = reinterpret_cast<const uint8_t *>(&f);
const auto sz = sizeof(T);
if (!specs.is_dynamic_precision() && !specs.is_dynamic_width()) {
const auto mb = gsnr::ehexdump_mb(d, specs.precision() ? specs.precision() : sz,
!specs.is_not_extended(), !specs.is_not_offset(),
specs.is_uppercase(), specs.width());
fmt::detail::copy<typename decltype(mb)::value_type()>(
mb.data(), mb.data() + mb.size(), ctx.out());
return ctx.out();
} else if (specs.is_dynamic_precision() && specs.is_dynamic_width()) {
const auto mb = gsnr::ehexdump_mb(d, dynamic_precision ? dynamic_precision : sz,
!specs.is_not_extended(), !specs.is_not_offset(),
specs.is_uppercase(), dynamic_width);
fmt::detail::copy<typename decltype(mb)::value_type()>(
mb.data(), mb.data() + mb.size(), ctx.out());
return ctx.out();
} else if (specs.is_dynamic_precision() && !specs.is_dynamic_width()) {
const auto precision_arg = ctx.arg(dynamic_precision_arg_id);
const auto precision_val =
dynamic_precision_arg_id >= 0
? ctx.arg(dynamic_precision_arg_id).visit(int_visitor())
: dynamic_precision;
fmt::print("dynamic_precision_arg_id: {} dynamic_precision_arg: {}\n",
dynamic_precision_arg_id, precision_val);
const auto mb =
gsnr::ehexdump_mb(d, std::min(precision_val, sz), !specs.is_not_extended(),
!specs.is_not_offset(), specs.is_uppercase(), specs.width());
fmt::detail::copy<typename decltype(mb)::value_type()>(
mb.data(), mb.data() + mb.size(), ctx.out());
return ctx.out();
} else if (!specs.is_dynamic_precision() && specs.is_dynamic_width()) {
const auto width_arg = ctx.arg(dynamic_width_arg_id);
const auto width_val = width_arg.visit(int_visitor());
fmt::print("dynamic_width_arg_id: {} dynamic_width_arg: {}\n", dynamic_width_arg_id,
width_val);
const auto mb = gsnr::ehexdump_mb(d, specs.precision() ? specs.precision() : sz,
!specs.is_not_extended(), !specs.is_not_offset(),
specs.is_uppercase(), width_val);
fmt::detail::copy<typename decltype(mb)::value_type()>(
mb.data(), mb.data() + mb.size(), ctx.out());
return ctx.out();
} else {
assert(!"dont reach me dynamic precision/width");
}
} else {
// return fmt::format_to(ctx.out(), f);
assert(!"not custom_ehexdump");
}
}
};
} // namespace gsnr
struct Foo {
uint32_t a;
uint8_t b;
};
struct Bar {
char fourcc[4];
void *ptr;
};
namespace fmt {
template <> struct formatter<Foo> : gsnr::generic_ehexdump_formatter<Foo> {};
template <> struct formatter<Bar> : gsnr::generic_ehexdump_formatter<Bar> {};
} // namespace fmt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment