rntviewer/src/hover.cpp

1055 lines
35 KiB
C++
Raw Normal View History

2024-10-16 10:00:38 +02:00
// C++ fuckery to get the number of lambda arguments ----
2024-09-27 15:09:16 +02:00
template <typename F>
struct Signature;
template <typename Obj, typename... Args>
struct Signature<void(Obj::*)(Args...) const> {
2024-10-16 10:00:38 +02:00
static constexpr u32 N_Args = sizeof...(Args);
2024-09-27 15:09:16 +02:00
};
template <typename F>
constexpr u32 n_functor_args = Signature<decltype(&std::decay_t<F>::operator())>::N_Args;
// -------- end C++ fuckery
// Dummy argument used by Sec_Hover_Fn::frame() to notify it wants to handle the for loop by itself.
// See comment in frame().
using Frame_List_Special_Handling = const void *;
// The data returned by get_section_hover_info(), i.e. the only reason why this entire file exists.
2024-09-10 16:59:22 +02:00
struct Sec_Hover_Info {
2024-09-27 15:09:16 +02:00
// Highlighted byte range
2024-09-10 16:59:22 +02:00
Byte_Range rng;
2024-09-27 15:09:16 +02:00
// A string tree where children are displayed as more indented than parents
2024-09-10 16:59:22 +02:00
String8_Node *desc;
2024-09-27 15:09:16 +02:00
// The one line of the tree that gets colored
2024-09-26 10:55:28 +02:00
String8_Node *highlighted_desc;
2024-09-10 16:59:22 +02:00
};
2024-09-27 15:09:16 +02:00
template <typename T> T bswap_if_needed(T x) {
if constexpr (sizeof(T) > 1 && std::is_integral_v<T>)
return bswap(x);
else
return x;
}
2024-09-10 16:59:22 +02:00
2024-10-16 10:00:38 +02:00
// Default display functions, used by the majority of fields
2024-09-10 16:59:22 +02:00
template <typename T>
String8_Node *hover_display_val_be(Arena *arena, String8_Node *prev, const char *fmt, T val)
{
2024-09-12 11:56:12 +02:00
static_assert(!std::is_same_v<T, String8>);
2024-09-10 16:59:22 +02:00
val = bswap_if_needed(val);
return push_str8_node_child(arena, prev, fmt, val);
}
2024-09-12 11:56:12 +02:00
String8_Node *hover_display_val_str8(Arena *arena, String8_Node *prev, const char *fmt, String8 val)
2024-09-10 16:59:22 +02:00
{
2024-09-12 11:56:12 +02:00
return push_str8_node_child(arena, prev, fmt, val.str ? val.c() : "");
2024-09-10 16:59:22 +02:00
}
template <typename T>
String8_Node *hover_display_val_le(Arena *arena, String8_Node *prev, const char *fmt, T val)
{
return push_str8_node_child(arena, prev, fmt, val);
}
template <typename T>
String8_Node *hover_display_val_le_abs(Arena *arena, String8_Node *prev, const char *fmt, T val)
{
return push_str8_node_child(arena, prev, fmt, std::abs(val));
}
2024-09-27 15:09:16 +02:00
String8_Node *hover_display_generic_range(Arena *arena, String8_Node *prev, const char *desc, const u8 *, u64 size)
2024-09-25 12:00:52 +02:00
{
2024-09-27 15:09:16 +02:00
return push_str8_node_child(arena, prev, push_str8f(arena, "%s (%s)", desc, to_pretty_size(arena, size).c()).c());
2024-09-25 12:00:52 +02:00
}
2024-09-10 16:59:22 +02:00
internal
String8_Node *hover_display_datetime_str(Arena *arena, String8_Node *prev, const char *fmt_pre, u32 datetime)
{
datetime = bswap(datetime);
// datetime:
// year (6b) | month (4b) | day (5b) | hour (5b) | min (6b) | sec (6b)
u32 year = (datetime >> 26) + 1995;
2024-09-16 15:55:32 +02:00
u32 month = ((datetime & 0x3ff'ffff) >> 22);
2024-09-10 16:59:22 +02:00
u32 day = (datetime & 0x3f'ffff) >> 17;
u32 hour = (datetime & 0x1'ffff) >> 12;
u32 min = (datetime & 0xfff) >> 6;
u32 sec = datetime & 0x3f;
return push_str8_node_child(arena, prev, "%s%u/%02u/%02u %02u:%02u:%02u", fmt_pre, year, month, day, hour, min, sec);
}
2024-09-25 14:57:06 +02:00
// Returns null is `src` doesn't point to a zipped block
2024-09-10 16:59:22 +02:00
internal
String8_Node *display_val_rootzip(Arena *arena, String8_Node *prev, const char *fmt, const u8 *src)
{
const u8 Z_DEFLATED = 8;
String8 zip_method;
if (src[0] == 'Z' && src[1] == 'L' && src[2] == Z_DEFLATED) {
zip_method = str8("ZLIB");
} else if (src[0] == 'C' && src[1] == 'S' && src[2] == Z_DEFLATED) {
zip_method = str8("Old");
} else if (src[0] == 'X' && src[1] == 'Z' && src[2] == 0) {
zip_method = str8("LZMA");
} else if (src[0] == 'L' && src[1] == '4') {
zip_method = str8("LZ4");
} else if (src[0] == 'Z' && src[1] == 'S' && src[2] == 1) {
zip_method = str8("ZSTD");
} else {
return nullptr;
}
u32 comp_size = src[3] | (src[4] << 8) | (src[5] << 16);
u32 uncomp_size = src[6] | (src[7] << 8) | (src[8] << 16);
String8_Node *sn = push_str8_node_child(arena, prev, "%s", fmt);
sn = push_str8_node_child(arena, sn, "Zip method: %s", zip_method.c());
sn = push_str8_node(arena, sn, "Compressed size: %s", to_pretty_size(arena, comp_size).c());
sn = push_str8_node(arena, sn, "Uncompressed size: %s", to_pretty_size(arena, uncomp_size).c());
2024-11-04 11:30:08 +01:00
sn = push_str8_node(arena, sn, "Comp. ratio: %f", (f32)comp_size / uncomp_size);
2024-09-10 16:59:22 +02:00
return sn;
}
template <typename T>
2024-10-16 10:00:38 +02:00
using Display_Fn = String8_Node *(*)(Arena *arena, String8_Node *prev, const char *fmt, T data);
using Display_Range_Fn = String8_Node *(*)(Arena *arena, String8_Node *prev, const char *fmt, const u8 *data, u64 len);
2024-09-27 15:09:16 +02:00
2024-09-25 14:57:06 +02:00
enum Hover_Section_Flags {
HoverSec_None = 0,
2024-09-27 17:10:53 +02:00
// Hide = don't show at all, even the title
2024-09-25 14:57:06 +02:00
HoverSec_HideIfNotHovered = 1,
2024-09-27 17:10:53 +02:00
// Collapse = only show the title
HoverSec_CollapseIfNotHovered = 2,
2024-09-25 14:57:06 +02:00
};
2024-10-31 14:50:19 +01:00
const u64 ROOTZIP_RANGE_LEN = 9;
2024-09-10 16:59:22 +02:00
// Functor used by get_section_hover_info to describe the structure of a section and print data about it.
struct Sec_Hover_Fn {
2024-09-27 16:23:50 +02:00
u64 off; // the hovered offset relative to the start of `data`
2024-09-25 10:07:42 +02:00
const u8 *data; // the entire file data
2024-10-16 10:00:38 +02:00
const Section &section; // the section we're hovering
2024-09-10 16:59:22 +02:00
Arena *arena;
2024-10-16 10:00:38 +02:00
Sec_Hover_Info &info; // our main output
u64 &cur_field_off; // current field offset relative to the start of `data`
2024-10-16 10:00:38 +02:00
// settings
2024-09-12 11:56:12 +02:00
b8 display_grouped;
2024-11-05 09:52:00 +01:00
b8 old_version; // if true, treat the RNTuple as 0.x rather than 1.x
2024-10-16 10:00:38 +02:00
// internals
2024-09-25 14:57:06 +02:00
u8 cur_section_nesting = 0;
u8 innermost_section_highlighted = 0;
2024-09-10 16:59:22 +02:00
2024-09-27 16:23:50 +02:00
template <typename T>
b8 read(T *val, u64 offset, u64 *size = nullptr) const
{
u64 nb = size ? *size : sizeof(T);
2024-09-27 17:10:53 +02:00
if (offset + nb > section.range.end()) {
fprintf(stderr, "Trying to read bytes 0x%" PRIX64 "-0x%" PRIX64 " which are past the end of the section 0x%" PRIX64 "!\n",
offset, offset + nb, section.range.end());
2024-09-27 16:23:50 +02:00
return false;
2024-09-27 17:10:53 +02:00
}
2024-09-27 16:23:50 +02:00
memcpy(val, data + offset, nb);
return true;
}
2024-09-10 16:59:22 +02:00
template <typename F>
2024-09-25 14:57:06 +02:00
void titled_section(const char *title, F &&sec_body_fn, u64 flags = 0)
2024-09-10 16:59:22 +02:00
{
2024-09-25 14:57:06 +02:00
++cur_section_nesting;
2024-09-10 16:59:22 +02:00
String8_Node *prev_desc = info.desc;
info.desc = push_str8_node_child(arena, prev_desc, title);
2024-09-25 12:00:52 +02:00
u64 sec_start = cur_field_off;
2024-09-10 16:59:22 +02:00
2024-09-25 14:57:06 +02:00
sec_body_fn();
2024-09-27 17:10:53 +02:00
// assert(cur_field_off >= sec_start);
if (cur_field_off < sec_start) {// TEMP DEBUG
fprintf(stderr, "Something wrong going on in %s!\n", title);
return;
}
b8 hovered = off >= sec_start && off <= cur_field_off;
2024-09-25 14:57:06 +02:00
2024-09-27 17:10:53 +02:00
if (!hovered) {
if (flags & HoverSec_HideIfNotHovered)
pop_str8_node_child(prev_desc, info.desc);
else if (flags & HoverSec_CollapseIfNotHovered)
info.desc->first_child = info.desc->last_child = nullptr;
2024-09-25 12:00:52 +02:00
} else if (display_grouped) {
// if we're in display_grouped mode, we want to highlight the entire range of the section;
u64 sec_len = cur_field_off - sec_start;
info.rng = { sec_start, sec_len };
2024-09-10 16:59:22 +02:00
}
2024-09-25 14:57:06 +02:00
2024-09-27 11:21:19 +02:00
if (!info.highlighted_desc) {
info.highlighted_desc = hovered ? info.desc : prev_desc;
innermost_section_highlighted = max(cur_section_nesting, innermost_section_highlighted);
} else if (display_grouped && innermost_section_highlighted <= cur_section_nesting && hovered) {
info.highlighted_desc = info.desc;
innermost_section_highlighted = max(cur_section_nesting, innermost_section_highlighted);
}
2024-09-25 14:57:06 +02:00
2024-09-27 11:21:19 +02:00
--cur_section_nesting;
2024-09-27 11:54:50 +02:00
// pop ourselves unless we're the top-level section
if (prev_desc)
info.desc = prev_desc;
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
// returns true if `val_read` was read
2024-09-12 11:56:12 +02:00
template <typename T>
2024-09-25 12:00:52 +02:00
b8 field(const char *desc_fmt, Display_Fn<T> display_val, T *val_read = nullptr)
2024-09-12 11:56:12 +02:00
{
2024-09-25 10:07:42 +02:00
static_assert(!std::is_same_v<T, String8>, "use field_str8 instead.");
2024-09-25 12:00:52 +02:00
2024-09-25 10:07:42 +02:00
u64 field_len = sizeof(T);
b8 hovered = cur_field_off <= off && off < cur_field_off + field_len;
2024-09-25 14:57:06 +02:00
T val;
2024-09-27 16:23:50 +02:00
if (!read(&val, cur_field_off))
return false;
2024-09-25 14:57:06 +02:00
String8_Node *desc = display_val(arena, info.desc, desc_fmt, val);
2024-09-26 10:55:28 +02:00
if (hovered && !display_grouped)
info.highlighted_desc = desc;
2024-09-25 14:57:06 +02:00
if (val_read)
*val_read = val;
if (display_grouped || hovered) {
info.rng = { cur_field_off, field_len };
2024-09-25 10:07:42 +02:00
}
2024-09-25 12:00:52 +02:00
cur_field_off += field_len;
2024-09-25 14:57:06 +02:00
return true;
2024-09-12 11:56:12 +02:00
}
template <typename TStrSize>
2024-09-25 12:00:52 +02:00
void field_str8(const char *desc_fmt, Display_Fn<String8> display_val = hover_display_val_str8)
2024-09-12 11:56:12 +02:00
{
2024-09-25 10:07:42 +02:00
// String size can be stored as different types, like u8 (by ROOT I/O) or u32 (by RNTuple).
TStrSize str_size;
2024-09-27 16:23:50 +02:00
if (!read(&str_size, cur_field_off))
return;
2024-09-25 12:00:52 +02:00
// DEBUG
if (str_size > 1000) {
printf("read str_size = %u at offset 0x%lX!\n", str_size, cur_field_off);
2024-09-25 12:00:52 +02:00
return;
}
2024-09-25 10:07:42 +02:00
u64 field_len = sizeof(TStrSize) + (u64)str_size;
b8 hovered = cur_field_off <= off && off < cur_field_off + field_len;
2024-09-25 14:57:06 +02:00
u8 *buf = arena_push_array_nozero<u8>(arena, str_size + 1);
2024-09-27 16:23:50 +02:00
u64 size_to_read = str_size;
if (!read(buf, cur_field_off + sizeof(TStrSize), &size_to_read))
return;
2024-09-25 14:57:06 +02:00
buf[str_size] = 0;
String8 s = { buf, str_size };
String8_Node *desc = display_val(arena, info.desc, desc_fmt, s);
2024-09-26 10:55:28 +02:00
if (hovered && !display_grouped)
info.highlighted_desc = desc;
2024-09-25 14:57:06 +02:00
if (display_grouped || hovered) {
info.rng = { cur_field_off, field_len };
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
cur_field_off += field_len;
2024-09-10 16:59:22 +02:00
}
template <typename T>
2024-09-25 12:00:52 +02:00
b8 field_be(const char *desc_fmt, T *val_read = nullptr)
2024-09-10 16:59:22 +02:00
{
2024-10-31 14:11:38 +01:00
b8 ok = field<T>(desc_fmt, hover_display_val_be<T>, val_read);
if constexpr (sizeof(T) > 1) {
if (ok && val_read)
*val_read = bswap(*val_read);
}
return ok;
2024-09-10 16:59:22 +02:00
}
template <typename T>
2024-09-25 12:00:52 +02:00
b8 field_le(const char *desc_fmt, T *val_read = nullptr)
2024-09-10 16:59:22 +02:00
{
2024-09-25 12:00:52 +02:00
return field<T>(desc_fmt, hover_display_val_le<T>, val_read);
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
// An unspecified range of bytes
2024-09-27 15:09:16 +02:00
void range(const char *desc, u64 range_len, Display_Range_Fn display_val = hover_display_generic_range)
2024-09-10 16:59:22 +02:00
{
2024-09-27 16:23:50 +02:00
if (cur_field_off + range_len > section.range.end())
return;
String8_Node *dsc = display_val(arena, info.desc, desc, data + cur_field_off, range_len);
2024-10-31 14:50:19 +01:00
b8 hovered = cur_field_off <= off && off < cur_field_off + range_len;
2024-09-26 10:55:28 +02:00
if (hovered && !display_grouped)
info.highlighted_desc = dsc;
2024-09-25 14:57:06 +02:00
if (display_grouped || hovered) {
info.rng = { cur_field_off, range_len };
2024-09-10 16:59:22 +02:00
}
cur_field_off += range_len;
}
2024-09-25 12:00:52 +02:00
// Returns true if `was_zipped` was read.
b8 maybe_rootzip(b8 *was_zipped = nullptr)
2024-09-10 16:59:22 +02:00
{
2024-10-31 14:50:19 +01:00
if (cur_field_off + ROOTZIP_RANGE_LEN > section.range.end())
2024-09-27 16:23:50 +02:00
return false;
2024-10-31 14:50:19 +01:00
b8 hovered = cur_field_off <= off && off < cur_field_off + ROOTZIP_RANGE_LEN;
if (display_val_rootzip(arena, info.desc, "Zipped Block", data + cur_field_off)) {
2024-09-27 16:23:50 +02:00
if (was_zipped)
*was_zipped = true;
2024-09-25 14:57:06 +02:00
if (display_grouped || hovered) {
2024-10-31 14:50:19 +01:00
info.rng = { cur_field_off, ROOTZIP_RANGE_LEN };
2024-09-26 10:55:28 +02:00
if (hovered)
info.highlighted_desc = info.desc;
2024-09-10 16:59:22 +02:00
}
2024-10-31 14:50:19 +01:00
cur_field_off += ROOTZIP_RANGE_LEN;
2024-09-10 16:59:22 +02:00
} else if (was_zipped) {
*was_zipped = false;
}
2024-09-25 12:00:52 +02:00
return true;
2024-09-10 16:59:22 +02:00
}
2024-09-27 11:54:50 +02:00
void tkey(const char *title = "TKey")
2024-09-10 16:59:22 +02:00
{
2024-09-27 11:54:50 +02:00
titled_section(title, [this] {
2024-09-10 16:59:22 +02:00
u16 version_be;
2024-09-27 16:23:50 +02:00
if (!read(&version_be, cur_field_off + 4))
return;
2024-09-10 16:59:22 +02:00
u32 version = bswap(version_be);
b8 is_big = version > 1000;
2024-09-26 14:11:25 +02:00
field_be<u32>("NBytes: %u");
field<u16>("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) {
x = bswap(x);
x -= (x > 1000) * 1000;
return push_str8_node_child(arena, prev, fmt, x);
});
field_be<u32>("Obj Len: %u");
field<u32>("Datetime: ", hover_display_datetime_str);
field_be<u16>("Key Len: %u");
field_be<u16>("Cycle: %u");
2024-09-10 16:59:22 +02:00
if (is_big) {
2024-09-25 12:00:52 +02:00
field_be<u64>("Seek Key: 0x%" PRIX64);
field_be<u64>("Seek Pdir: 0x%" PRIX64);
2024-09-10 16:59:22 +02:00
} else {
2024-09-25 12:00:52 +02:00
field_be<u32>("Seek Key: 0x%" PRIX64);
field_be<u32>("Seek Pdir: 0x%" PRIX64);
2024-09-10 16:59:22 +02:00
}
2024-09-26 14:11:25 +02:00
field_str8<u8>("Class Name: %s");
field_str8<u8>("Obj Name: %s");
field_str8<u8>("Obj Title: %s");
2024-09-26 10:55:28 +02:00
}, HoverSec_HideIfNotHovered);
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
void envelope_preamble()
2024-09-10 16:59:22 +02:00
{
static const char *const envelope_names[] = { "INVALID", "Header", "Footer", "Page List" };
2024-09-25 12:00:52 +02:00
titled_section("Envelope Preamble", [this] {
field<u16>("Envelope type: %s", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 val) {
const char *name = (val >= countof(envelope_names)) ? "Unknown" : envelope_names[val];
return push_str8_node_child(arena, prev, fmt, name);
});
2024-09-27 15:09:16 +02:00
range("Envelope size: %s", 6, [] (Arena *arena, String8_Node *prev, const char *fmt, const u8 *payload, u64) {
2024-09-25 12:00:52 +02:00
u64 size;
memcpy(&size, payload, 6);
2024-09-27 16:23:50 +02:00
return push_str8_node_child(arena, prev, fmt, to_pretty_size(arena, size).c());
2024-09-25 12:00:52 +02:00
});
2024-09-20 11:26:31 +02:00
});
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
enum Frame_Type {
2024-09-25 14:23:28 +02:00
Frame_INVALID,
2024-09-25 12:00:52 +02:00
Frame_Record,
2024-09-27 17:10:53 +02:00
Frame_List,
Frame_COUNT
};
static constexpr const char *frame_type_str[Frame_COUNT] = {
"INVALID", "Record", "List"
2024-09-25 12:00:52 +02:00
};
Frame_Type frame_header(u64 &size, u32 *n_items = nullptr, const char *title = nullptr)
2024-09-10 16:59:22 +02:00
{
2024-09-20 11:26:31 +02:00
String8 titlestr = title ? push_str8f(arena, "Frame Header: %s", title) : str8("Frame Header");
2024-09-25 14:23:28 +02:00
Frame_Type frame_type = Frame_INVALID;
2024-09-26 14:11:25 +02:00
titled_section(titlestr.c(), [this, &frame_type, &frame_size = size, n_items] {
2024-09-20 11:26:31 +02:00
i64 size;
2024-09-27 16:23:50 +02:00
if (!read(&size, cur_field_off)) {
frame_size = 0;
frame_type = Frame_INVALID;
return;
}
2024-09-27 15:09:16 +02:00
// Sanity check
if (size > 1000000) {
fprintf(stderr, "Frame size read at 0x%" PRIX64 " looks bogus"
" (is it really %s? Don't think so...); setting it to 0 for good measure.\n",
cur_field_off, to_pretty_size(arena, size).c());
2024-09-27 15:09:16 +02:00
frame_size = 0;
frame_type = Frame_INVALID;
return;
}
2024-09-25 12:00:52 +02:00
if (size >= 0) {
frame_type = Frame_Record;
field<i64>("Record frame size: %" PRIi64 " B", hover_display_val_le_abs<i64>);
} else {
2024-09-26 14:11:25 +02:00
if (!n_items) {
// Since the caller didn't pass us a pointer to n_items, they think we're supposed
// to be parsing a record frame. But it turns out this is actually a frame list!
// Something fishy is going on, so just bail out.
frame_type = Frame_INVALID;
} else {
frame_type = Frame_List;
2024-09-27 16:23:50 +02:00
if (!read(n_items, cur_field_off + sizeof(i64))) {
frame_size = 0;
frame_type = Frame_INVALID;
return;
}
2024-09-27 15:09:16 +02:00
field<i64>("List frame size: %" PRIi64 " B", hover_display_val_le_abs<i64>);
field_le<u32>("List frame n.items: %u");
2024-09-26 14:11:25 +02:00
}
2024-09-20 11:26:31 +02:00
}
2024-09-25 12:00:52 +02:00
frame_size = std::abs(size);
2024-09-10 16:59:22 +02:00
});
2024-09-27 15:09:16 +02:00
2024-09-25 12:00:52 +02:00
return frame_type;
2024-09-10 16:59:22 +02:00
}
2024-09-26 14:11:25 +02:00
void field_desc(const char *title)
2024-09-10 16:59:22 +02:00
{
static const char *const field_struct_names[] = {
"Leaf", "Collection", "Record", "Variant", "Unsplit"
};
2024-09-12 11:56:12 +02:00
2024-09-26 14:11:25 +02:00
frame<Frame_Record>(title, [this] {
2024-09-25 12:00:52 +02:00
field_le<u32>("Field version: %u");
field_le<u32>("Type version: %u");
field_le<u32>("On-disk parent id: %u");
field<u16>("Field structure: %s", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 type) {
const char *name = (type >= countof(field_struct_names)) ? "Unknown" : field_struct_names[type];
return push_str8_node_child(arena, prev, fmt, name);
2024-09-12 11:56:12 +02:00
});
2024-09-25 12:00:52 +02:00
u16 flags;
if (!field<u16>("Flags: 0b%b", hover_display_val_le, &flags))
return;
2024-11-05 09:52:00 +01:00
if (old_version) {
if (flags & RNTupleSerializer::kFlagRepetitiveField)
field_le<u64>("N Repetitions: %" PRIu64);
if (flags & RNTupleSerializer::kFlagProjectedField)
field_le<u32>("On disk proj.src id: %u");
if (flags & RNTupleSerializer::kFlagHasTypeChecksum)
field_le<u32>("Checksum: %u");
}
2024-09-25 12:00:52 +02:00
field_str8<u32>("Name: %s");
field_str8<u32>("Type Name: %s");
field_str8<u32>("Type Alias: %s");
field_str8<u32>("Description: %s");
2024-11-05 09:52:00 +01:00
if (!old_version) {
if (flags & RNTupleSerializer::kFlagRepetitiveField)
field_le<u64>("N Repetitions: %" PRIu64);
if (flags & RNTupleSerializer::kFlagProjectedField)
field_le<u32>("On disk proj.src id: %u");
if (flags & RNTupleSerializer::kFlagHasTypeChecksum)
field_le<u32>("Checksum: %u");
}
2024-09-27 17:10:53 +02:00
});
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
void column_desc(const char *title)
2024-09-10 16:59:22 +02:00
{
2024-09-26 14:11:25 +02:00
frame<Frame_Record>(title, [this] {
2024-09-25 12:00:52 +02:00
field<u16>("Column type: %s", [](Arena *arena, String8_Node *prev, const char *fmt, u16 val) {
2024-10-16 10:00:38 +02:00
const char *readable_col_type = get_column_type_name_from_ondisk_type(val);
2024-09-25 12:00:52 +02:00
return push_str8_node_child(arena, prev, fmt, readable_col_type);
2024-09-25 10:07:42 +02:00
});
2024-09-25 12:00:52 +02:00
field_le<u16>("Bits on storage: %u");
field_le<u32>("Field ID: %u");
u16 flags;
if (!field<u16>("Flags: 0b%b", hover_display_val_le, &flags))
return;
field_le<u16>("Representation idx: %u");
if (flags & RNTupleSerializer::kFlagDeferredColumn) {
field_le<u64>("First element: %" PRIu64);
}
2024-09-27 15:09:16 +02:00
if (flags & RNTupleSerializer::kFlagHasValueRange) {
field_le<double>("Value Min: %f");
field_le<double>("Value Max: %f");
}
2024-09-27 17:10:53 +02:00
});
2024-09-10 16:59:22 +02:00
}
2024-09-25 12:00:52 +02:00
void schema_description(const char *title)
2024-09-16 15:55:32 +02:00
{
2024-09-25 12:00:52 +02:00
titled_section(title, [this] {
2024-09-27 17:10:53 +02:00
frame<Frame_List>("Fields", [this] (u32 idx) {
field_desc(push_str8f(arena, "Field %u", idx).c());
});
2024-09-26 14:11:25 +02:00
frame<Frame_List>("Columns", [this] (u32 idx) { column_desc(push_str8f(arena, "Column %u", idx).c()); });
2024-09-27 15:09:16 +02:00
frame<Frame_List>("Alias Columns", [this] (u32 idx) {
frame<Frame_Record>(push_str8f(arena, "Alias Column %u", idx).c(), [this] {
field_le<u32>("Phys Col Id: %u");
field_le<u32>("Field Id: %u");
});
});
frame<Frame_List>("Extra Type Infos", [this] (u32 idx) {
frame<Frame_Record>(push_str8f(arena, "Extra Type Info %u", idx).c(), [this] {
field_le<u32>("Content identifier: %lu");
2024-11-05 09:52:00 +01:00
if (old_version) {
field_le<u32>("Type version from: %lu");
field_le<u32>("Type version to: %lu");
} else {
field_le<u32>("Type version: %lu");
}
2024-09-27 15:09:16 +02:00
});
2024-09-25 12:00:52 +02:00
});
2024-09-10 16:59:22 +02:00
});
}
2024-09-20 11:26:31 +02:00
2024-09-25 12:00:52 +02:00
void locator(const char *title)
2024-09-20 11:26:31 +02:00
{
2024-09-27 15:09:16 +02:00
titled_section(title, [this] {
i32 head;
b8 ok = field<i32>("", [] (Arena *arena, String8_Node *prev, const char *, i32 head) {
if (head < 0) {
head = -head;
i32 type = head >> 24;
switch (type) {
case 0x01: return push_str8_node_child(arena, prev, "Type: Large File");
case 0x02: return push_str8_node_child(arena, prev, "Type: DAOS");
default: return push_str8_node_child(arena, prev, "Type: Unknown");
}
} else {
return push_str8_node_child(arena, prev, "Type: File");
}
}, &head);
if (!ok)
return;
if (head < 0) {
head = -head;
i32 type = head >> 24;
u32 size = (u32(head) & 0xffff) - sizeof(i32);
u32 reserved = (head >> 16) & 0xff;
push_str8_node_child(arena, info.desc, "Size: %u", size);
push_str8_node_child(arena, info.desc, "Reserved: %u", reserved);
switch (type) {
case 0x01:
field_le<u64>("N Bytes: %" PRIu64);
field_le<u64>("Position: 0x%" PRIX64);
break;
case 0x02:
if (size == 12) {
field_le<u32>("N Bytes: %u");
field_le<u64>("Location: 0x%" PRIX64);
} else if (size == 16) {
field_le<u64>("N Bytes: %" PRIu64);
field_le<u64>("Location: 0x%" PRIX64);
} else {
range("Unknown payload", size);
}
break;
default:
range("Unknown locator", size);
}
} else {
push_str8_node_child(arena, info.desc, "N Bytes: %" PRIu64, head);
field_le<u64>("Position: 0x%" PRIX64);
}
});
2024-09-25 12:00:52 +02:00
}
void cluster_group()
{
2024-09-27 17:10:53 +02:00
frame<Frame_Record>("Cluster Group", [this] {
2024-09-26 14:11:25 +02:00
field_le<u64>("Min Entry: %" PRIu64);
field_le<u64>("Entry Span: %" PRIu64);
field_le<u32>("N Clusters: %u");
field_le<u64>("Env.Link Len: %" PRIu64);
locator("Env.Link Locator");
2024-09-25 12:00:52 +02:00
});
}
2024-09-26 14:11:25 +02:00
void cluster_summary()
{
frame<Frame_Record>("Cluster Summary", [this] {
field_le<u64>("First Entry: %" PRIu64);
field<u64>("", [] (Arena *arena, String8_Node *prev, const char *, u64 x) {
u64 entries = (x << 8) >> 8;
u8 flags = x >> 56;
String8_Node *sn = push_str8_node_child(arena, prev, "Entries: %" PRIu64, entries);
return push_str8_node(arena, sn, "Flags: 0b%b", flags);
});
});
}
void cluster()
{
2024-09-27 15:09:16 +02:00
frame<Frame_List>("Cluster", [this] (u32 col_idx) { // outer list of columns
2024-09-26 14:11:25 +02:00
titled_section(push_str8f(arena, "Column %u", col_idx).c(), [this] {
2024-09-27 15:09:16 +02:00
// Inner list of pages. NOTE this is a mischievous list frame who needs special handling!
// See the comment in frame() for more details.
frame<Frame_List>("Pages", [this] (u32 n_items, Frame_List_Special_Handling) {
for (u32 page_idx = 0; page_idx < n_items; ++page_idx) {
titled_section(push_str8f(arena, "Page %u", page_idx).c(), [this] {
i32 n_elems;
if (!field<i32>("N Elements: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, i32 n_elems) {
return push_str8_node_child(arena, prev, fmt, (u32)std::abs(n_elems));
}, &n_elems))
{
return;
}
2024-09-26 14:11:25 +02:00
b8 has_checksum = n_elems < 0;
2024-09-27 15:09:16 +02:00
push_str8_node_child(arena, info.desc, "Has Checksum: %s", has_checksum ? "yes" : "no");
locator("Element Locator");
2024-09-27 17:10:53 +02:00
});
2024-09-27 15:09:16 +02:00
}
2024-09-26 14:11:25 +02:00
2024-09-27 15:09:16 +02:00
i64 n_cols;
if (!field<i64>("", [] (Arena *arena, String8_Node *prev, const char *, i64 n_cols) {
if (n_cols < 0) {
return push_str8_node_child(arena, prev, "Element Offset: <suppressed>");
}
return push_str8_node_child(arena, prev, "Element Offset: %" PRIi64, n_cols);
}, &n_cols))
{
return;
2024-09-26 14:11:25 +02:00
}
2024-09-27 15:09:16 +02:00
if (n_cols >= 0)
field_le<i32>("Compression Settings: %d");
});
2024-09-27 17:10:53 +02:00
});
2024-09-26 14:11:25 +02:00
});
}
template <Frame_Type FType, typename F>
2024-09-27 17:10:53 +02:00
void frame(const char *title, F &&frame_body_fn, u64 sec_flags = HoverSec_CollapseIfNotHovered)
2024-09-26 14:11:25 +02:00
{
u64 start_off = cur_field_off;
u64 size;
titled_section(title, [this, title, start_off, &size, &frame_body_fn] {
u32 n_items = 0;
Frame_Type ftype = frame_header(size, &n_items);
2024-09-27 17:10:53 +02:00
if (ftype != FType) {
fprintf(stderr, "Frame %s was supposed to be of type %s but it's of type %s\n",
title, frame_type_str[FType], frame_type_str[ftype]);
2024-09-26 14:11:25 +02:00
return;
2024-09-27 17:10:53 +02:00
}
2024-09-26 14:11:25 +02:00
if constexpr (FType == Frame_List) {
2024-09-27 15:09:16 +02:00
// Sadness here.
// Here's the thing: for convenience, when we deal with a Frame_List, we want to pass
// a function that handles the single element, so we don't have to repeat the for loop
// in every lambda we pass to frame<Frame_List>.
// However, there is an oddball case where a list frame declares a size that's not simply
// the sum of all its elements, but it also includes trailing stuff (looking at you, Page Locations frame:
// https://github.com/root-project/root/blob/master/tree/ntuple/v7/doc/specifications.md#page-locations)
// So, to avoid bloating all other well-behaving list frames' code, we allow passing a lambda that
// handles the entire thing, similarly to what we do for Frame_Record.
// The way we distinguish the case is by checking if the given lambda accepts only a u32 param (regular case)
// or exactly 2 arguments (oddball case). In this second case, the first u32 gives the number of items,
// instead of the element index, and the second argument has no meaning.
constexpr u32 n_fn_args = n_functor_args<F>;
if constexpr (n_fn_args == 1) {
for (u32 i = 0; i < n_items; ++i)
frame_body_fn(i);
} else if constexpr (n_fn_args == 2) {
frame_body_fn(n_items, nullptr);
} else {
static_assert(!sizeof(F), "frame_body_fn must accept either 1 (regular case) or 2 arguments!");
}
2024-09-26 14:11:25 +02:00
} else {
frame_body_fn();
}
assert(cur_field_off >= start_off);
u64 allocated_size = cur_field_off - start_off;
if (size < allocated_size) {
fprintf(stderr, "Frame %s told us its size was %" PRIu64 " but we accounted for %" PRIu64 " bytes!\n",
title, size, allocated_size);
}
u64 extra_size = size - allocated_size;
if (extra_size > 0)
2024-09-27 15:09:16 +02:00
range(push_str8f(arena, "Unknown frame extra payload of %s", title).c(), extra_size);
2024-09-26 14:11:25 +02:00
}, sec_flags);
cur_field_off = start_off + size;
}
2024-09-27 11:54:50 +02:00
// ==============================================================
// TOP-LEVEL SECTIONS
// ==============================================================
2024-09-25 12:00:52 +02:00
void tfile_header()
{
titled_section("TFile Header", [this] {
u32 root_version_be;
2024-09-27 16:23:50 +02:00
if (!read(&root_version_be, cur_field_off + 4))
return;
2024-09-25 12:00:52 +02:00
u32 root_version = bswap(root_version_be);
2024-09-26 14:11:25 +02:00
b8 is_big = root_version > 1000'000;
2024-09-25 12:00:52 +02:00
2024-09-26 14:11:25 +02:00
field_be<u32>("ROOT magic number");
field<u32>("ROOT version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) {
x = bswap(x);
x -= (x > 1000'000) * 1000'000;
return push_str8_node_child(arena, prev, fmt, x);
});
field_be<u32>("fBEGIN: 0x%" PRIX64);
2024-09-25 12:00:52 +02:00
if (is_big) {
field_be<u64>("fEND: 0x%" PRIX64);
field_be<u64>("Seek Free: 0x%" PRIX64);
} else {
field_be<u32>("fEND: 0x%" PRIX64);
field_be<u32>("Seek Free: 0x%" PRIX64);
}
2024-09-26 14:11:25 +02:00
field_be<u32>("NBytes Free: %u");
field_be<u32>("N Free: %u");
field_be<u32>("NBytes Name: %u");
field_be<u8>("Units: %u");
field_be<u32>("Compression: %u");
if (is_big)
field_be<u64>("Seek Info: 0x%" PRIX64);
else
field_be<u32>("Seek Info: 0x%" PRIX64);
field_be<u32>("NBytes Info: %u");
range("Padding", section.post_size);
2024-09-25 12:00:52 +02:00
});
}
void tfile_object()
{
titled_section("TFile Object", [this] {
tkey();
field_str8<u8>("File Name: %s");
field_str8<u8>("File Title: %s");
u16 version_be;
2024-09-27 16:23:50 +02:00
if (!read(&version_be, cur_field_off))
return;
2024-09-25 12:00:52 +02:00
u16 version = bswap(version_be);
b8 is_big = version > 1000;
2024-09-26 14:11:25 +02:00
field<u16>("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) {
x = bswap(x);
x -= (x > 1000) * 1000;
return push_str8_node_child(arena, prev, fmt, x);
});
field<u32>("Created: ", hover_display_datetime_str);
field<u32>("Modified: ", hover_display_datetime_str);
field_be<u32>("NBytes Key: %u");
field_be<u32>("NBytes Name: %u");
2024-09-25 12:00:52 +02:00
if (is_big) {
field_be<u64>("Seek Dir: 0x%" PRIX64) ;
field_be<u64>("Seek Parent: 0x%" PRIX64) ;
field_be<u64>("Seek Keys: 0x%" PRIX64) ;
} else {
field_be<u32>("Seek Dir: 0x%" PRIX64) ;
field_be<u32>("Seek Parent: 0x%" PRIX64) ;
field_be<u32>("Seek Keys: 0x%" PRIX64) ;
2024-09-26 14:11:25 +02:00
}
field_be<u16>("UUID Vers.Class: %u");
field_le<u16>("UUID: %u");
if (!is_big)
2024-09-25 12:00:52 +02:00
range("Padding", 3 * sizeof(u32));
2024-09-26 14:11:25 +02:00
});
}
2024-09-27 11:54:50 +02:00
2024-09-26 14:11:25 +02:00
void tfile_info()
{
2024-09-27 11:54:50 +02:00
titled_section("TFile Streamer Info", [this] {
2024-09-26 14:11:25 +02:00
tkey();
b8 zipped;
if (!maybe_rootzip(&zipped))
return;
if (zipped) {
2024-10-31 14:50:19 +01:00
range("Compressed Payload", section.range.len - ROOTZIP_RANGE_LEN);
2024-09-26 14:11:25 +02:00
} else {
field<u32>("Byte Count: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) {
x = bswap(x);
2024-09-27 11:54:50 +02:00
x -= 0x4000'0000;
2024-09-26 14:11:25 +02:00
return push_str8_node_child(arena, prev, fmt, x);
});
field_be<u16>("Version: %u");
2024-09-27 11:54:50 +02:00
range("TObject Data", section.range.len - 6);
2024-09-26 14:11:25 +02:00
}
});
}
2024-09-27 11:54:50 +02:00
2024-09-26 14:11:25 +02:00
void tfile_freelist()
{
titled_section("TFile FreeList", [this] {
tkey();
u16 version_be;
2024-09-27 16:23:50 +02:00
if (!read(&version_be, cur_field_off))
return;
2024-09-26 14:11:25 +02:00
u32 version = bswap(version_be);
b8 is_big = version > 1000;
2024-11-06 14:58:14 +01:00
// FIXME: this is not correct in all cases
2024-11-04 11:10:27 +01:00
while (cur_field_off < section.range.end()) {
titled_section("Free Slot", [this, is_big] {
field<u16>("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) {
x = bswap(x);
x -= (x > 1000) * 1000;
return push_str8_node_child(arena, prev, fmt, x);
});
if (is_big) {
field_be<u64>("First: 0x%" PRIX64);
field_be<u64>("Last: 0x%" PRIX64);
} else {
field_be<u32>("First: 0x%X");
field_be<u32>("Last: 0x%X");
}
}, HoverSec_CollapseIfNotHovered);
2024-09-25 12:00:52 +02:00
}
2024-09-20 11:26:31 +02:00
});
}
2024-09-27 11:54:50 +02:00
void tkey_list()
{
titled_section("TKey List", [this] {
tkey();
2024-10-31 14:11:38 +01:00
u32 n_keys;
if (field_be<u32>("N Keys: %u", &n_keys)) {
for (u32 i = 0; i < n_keys; ++i)
tkey();
}
2024-09-27 11:54:50 +02:00
});
}
void rntuple_anchor()
{
titled_section("RNTuple Anchor", [this] {
tkey();
titled_section("Data", [this] {
field<u32>("Object len: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) {
x = bswap(x);
x -= 0x4000'0000;
return push_str8_node_child(arena, prev, fmt, x);
});
field_be<u16>("Class version: %u");
field_be<u16>("Version Epoch: %u");
field_be<u16>("Version Major: %u");
field_be<u16>("Version Minor: %u");
field_be<u16>("Version Patch: %u");
field_be<u64>("Seek Header: 0x%" PRIX64);
field_be<u64>("NBytes Header: %u");
field_be<u64>("Len Header: %u");
field_be<u64>("Seek Footer: 0x%" PRIX64);
field_be<u64>("NBytes Footer: %u");
field_be<u64>("Len Footer: %u");
field_be<u64>("Max Key Size: %u");
field_le<u64>("Checksum: 0x%" PRIX64);
}, HoverSec_HideIfNotHovered);
});
}
void rntuple_header()
{
titled_section("RNTuple Header", [this] {
tkey();
titled_section("Data", [this] {
b8 zipped;
if (!maybe_rootzip(&zipped))
return;
if (zipped) {
2024-10-31 14:50:19 +01:00
range("Compressed payload", section.range.len - section.post_size - ROOTZIP_RANGE_LEN);
2024-09-27 11:54:50 +02:00
} else {
envelope_preamble();
// NOTE: flags in principle require a more complex handling, but for now they are unused,
// so they're always occupying only 8 bytes.
field_le<u64>("Flags: 0x%" PRIX64);
field_str8<u32>("Name: %s");
field_str8<u32>("Description: %s");
field_str8<u32>("ROOT version: %s");
schema_description("Schema Description");
}
2024-09-27 17:10:53 +02:00
field_le<u64>("Checksum: 0x%" PRIX64);
2024-09-27 11:54:50 +02:00
}, HoverSec_HideIfNotHovered);
});
}
void rntuple_footer()
{
titled_section("RNTuple Footer", [this] {
tkey();
titled_section("Data", [this] {
b8 zipped;
if (!maybe_rootzip(&zipped))
return;
if (zipped) {
2024-10-31 14:50:19 +01:00
range("Payload", section.range.len - section.post_size - ROOTZIP_RANGE_LEN);
2024-09-27 11:54:50 +02:00
} else {
envelope_preamble();
2024-09-27 17:18:03 +02:00
// TODO: flags in principle require a more complex handling, but for now they are unused,
2024-09-27 11:54:50 +02:00
// so they're always occupying only 8 bytes.
field_le<u64>("Flags: 0x%" PRIX64);
field_le<u64>("Header checksum: 0x%" PRIX64);
2024-09-27 17:10:53 +02:00
frame<Frame_Record>("Schema Extension", [this] {
schema_description("Schema Extension");
});
2024-11-05 09:52:00 +01:00
if (old_version) {
frame<Frame_List>("Column Groups", [this] (u32) {
field_le<u32>("Column Id: %u");
});
}
2024-09-27 17:10:53 +02:00
frame<Frame_List>("Cluster Groups", [this] (u32) {
cluster_group();
});
2024-09-27 11:54:50 +02:00
}
2024-09-27 17:10:53 +02:00
field_le<u64>("Checksum: 0x%" PRIX64);
2024-09-27 11:54:50 +02:00
}, HoverSec_HideIfNotHovered);
});
}
void page()
{
2024-10-16 10:00:38 +02:00
titled_section("Page", [this] {
// only try hovering a key if this is the first page of the cluster (<=> pre_size != 0)
if (section.pre_size)
tkey();
b8 zipped;
if (!maybe_rootzip(&zipped))
return;
2024-10-31 14:50:19 +01:00
range("Payload", section.range.len - section.post_size - ROOTZIP_RANGE_LEN); // TODO: improve
b8 has_checksum = ((Page_Info_Node *)section.info)->n_elems < 0;
if (has_checksum)
field_le<u64>("Checksum: 0x%" PRIX64);
2024-10-16 10:00:38 +02:00
});
2024-09-27 11:54:50 +02:00
}
void page_list()
{
titled_section("Page List", [this] {
tkey();
titled_section("Data", [this] {
b8 zipped;
if (!maybe_rootzip(&zipped))
return;
if (zipped) {
2024-10-31 14:50:19 +01:00
range("Payload", section.range.len - section.post_size - ROOTZIP_RANGE_LEN);
2024-09-27 11:54:50 +02:00
} else {
envelope_preamble();
field_le<u64>("Header checksum: 0x%" PRIX64);
frame<Frame_List>("Cluster Summaries", [this] (u32) { cluster_summary(); });
frame<Frame_List>("Clusters", [this] (u32) { cluster(); });
}
2024-09-27 17:10:53 +02:00
field_le<u64>("Checksum: 0x%" PRIX64);
2024-09-27 11:54:50 +02:00
}, HoverSec_HideIfNotHovered);
});
}
2024-09-10 16:59:22 +02:00
};
// `off` is the absolute offset into `data`.
internal
2024-11-05 09:52:00 +01:00
Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, const u8 *data, b8 display_grouped, b8 old_version)
2024-09-10 16:59:22 +02:00
{
Sec_Hover_Info info {};
// printf("off: 0x%" PRIX64 ", sec start - pre_size: (0x%" PRIX64 " - %" PRIu64 ") = 0x%" PRIX64 "\n", off, section.range.start, section.pre_size, section.range.start - section.pre_size);
assert(off >= section.range.start - section.pre_size);
// Hover info header
String8 sec_name = section_names[section.id];
if (section.id == Sec_Page && section.info) {
Page_Info_Node *pinfo = (Page_Info_Node *)section.info;
info.desc = push_str8_node(arena, nullptr, "%s [%s]",
sec_name.c(), pinfo->elem_type_name.c());
push_str8_node_child(arena, info.desc, "Field: %s", pinfo->owner_field_name.c());
push_str8_node_child(arena, info.desc, "N. Elems: %d", abs(pinfo->n_elems));
push_str8_node_child(arena, info.desc, "Bits per elem: %u", pinfo->bits_per_elem);
push_str8_node_child(arena, info.desc, "-----------");
}
u64 cur_field_off = section.range.start - section.pre_size;
2024-11-05 09:52:00 +01:00
Sec_Hover_Fn hover { off, data, section, arena, info, cur_field_off, display_grouped, old_version };
2024-09-10 16:59:22 +02:00
switch (section.id) {
2024-09-27 11:54:50 +02:00
case Sec_RNTuple_Anchor:
2024-09-25 12:00:52 +02:00
hover.rntuple_anchor();
2024-09-27 11:54:50 +02:00
break;
2024-09-10 16:59:22 +02:00
2024-09-27 11:54:50 +02:00
case Sec_TFile_Header:
2024-09-25 12:00:52 +02:00
hover.tfile_header();
2024-09-27 11:54:50 +02:00
break;
2024-09-10 16:59:22 +02:00
2024-09-27 11:54:50 +02:00
case Sec_TFile_Object:
2024-09-25 12:00:52 +02:00
hover.tfile_object();
2024-09-27 11:54:50 +02:00
break;
2024-09-25 12:00:52 +02:00
2024-09-27 11:54:50 +02:00
case Sec_RNTuple_Header:
2024-09-25 12:00:52 +02:00
hover.rntuple_header();
2024-09-27 11:54:50 +02:00
break;
2024-09-10 16:59:22 +02:00
2024-09-27 11:54:50 +02:00
case Sec_RNTuple_Footer:
2024-09-25 12:00:52 +02:00
hover.rntuple_footer();
2024-09-27 11:54:50 +02:00
break;
2024-09-25 12:00:52 +02:00
2024-09-27 11:54:50 +02:00
case Sec_Page_List:
2024-09-26 10:55:28 +02:00
hover.page_list();
2024-09-27 11:54:50 +02:00
break;
2024-09-10 16:59:22 +02:00
2024-09-27 11:54:50 +02:00
case Sec_Page:
hover.page();
break;
case Sec_TFile_Info:
2024-09-26 14:11:25 +02:00
hover.tfile_info();
2024-09-27 11:54:50 +02:00
break;
2024-09-10 16:59:22 +02:00
2024-09-27 11:54:50 +02:00
case Sec_TFile_FreeList:
2024-09-26 14:11:25 +02:00
hover.tfile_freelist();
2024-09-27 11:54:50 +02:00
break;
2024-09-10 16:59:22 +02:00
2024-09-27 11:54:50 +02:00
case Sec_TKey_List:
hover.tkey_list();
break;
default:
info.desc = push_str8_node(arena, nullptr, "%s", sec_name.c());
2024-09-10 16:59:22 +02:00
}
2024-09-27 11:54:50 +02:00
assert(info.desc);
2024-09-27 11:21:19 +02:00
// If we're displaying individual values, only show the ancestry of the highlighted desc and its siblings.
// @Speed: there is probably a more efficient way to do this by construction.
if (info.highlighted_desc && !display_grouped) {
String8_Node *cur = info.highlighted_desc;
// keep the siblings of highlighted desc, but drop their children.
cur = cur->parent;
if (cur) {
for (String8_Node *child = cur->first_child; child; child = child->next) {
if (child != info.highlighted_desc)
child->first_child = child->last_child = nullptr;
}
while (cur->parent) {
// discard all other children
String8_Node *parent = cur->parent;
parent->first_child = parent->last_child = cur;
cur->next = nullptr;
cur = parent;
}
}
}
2024-09-10 16:59:22 +02:00
return info;
}