From 705b4b353d9f79b3441986b3aaba11d4aec02ccd Mon Sep 17 00:00:00 2001 From: silverweed Date: Tue, 10 Sep 2024 15:56:21 +0200 Subject: [PATCH] start reading header hover info --- src/render.cpp | 2 +- src/rntuple.cpp | 389 +++++++++++++++++++++++++++++------------------- src/str.cpp | 14 ++ src/types.h | 1 - 4 files changed, 254 insertions(+), 152 deletions(-) diff --git a/src/render.cpp b/src/render.cpp index 8cf1239..fce40bb 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -388,7 +388,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) { Section hovered_section = find_section(app, hovered_off - 1); Sec_Hover_Info hover_info = get_section_hover_info(scratch.arena, hovered_section, hovered_off - 1, app.inspected_file.mem); - imgui_render_string_tree(scratch.arena, hover_info.desc); + imgui_render_string_tree(scratch.arena, hover_info.desc->head); app.viewer.hovered_range = hover_info.rng; } else { app.viewer.hovered_range = {}; diff --git a/src/rntuple.cpp b/src/rntuple.cpp index a5ebf97..0e3bcb6 100644 --- a/src/rntuple.cpp +++ b/src/rntuple.cpp @@ -653,6 +653,12 @@ String8_Node *hover_display_val_le(Arena *arena, String8_Node *prev, const char return push_str8_node_child(arena, prev, fmt, val); } +template +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)); +} + internal String8_Node *hover_display_datetime_str(Arena *arena, String8_Node *prev, const char *fmt_pre, u32 datetime) { @@ -704,7 +710,7 @@ template using Display_Fn = String8_Node *(*)(Arena *, String8_Node *, const char *, T); // Functor used by get_section_hover_info to describe the structure of a section and print data about it. -struct Try_Sec_Hover_Fn { +struct Sec_Hover_Fn { u64 start; u64 roff; const u8 *data; @@ -713,37 +719,49 @@ struct Try_Sec_Hover_Fn { u64 &cur_field_off; template - b8 field(const char *desc_fmt, Display_Fn display_val = hover_display_val_be) const + b8 field(const char *desc_fmt, Display_Fn display_val) const { - if constexpr (std::is_same_v) { - return field_str8(desc_fmt, display_val); - } else { - u64 field_len = sizeof(T); - if (roff < cur_field_off + field_len) { - info.rng = { start + cur_field_off, field_len }; - T val; - memcpy(&val, data + info.rng.start, info.rng.len); - display_val(arena, info.desc, desc_fmt, val); - return true; - } - cur_field_off += field_len; - return false; + static_assert(!std::is_same_v, "use field_str8 instead."); + u64 field_len = sizeof(T); + if (roff < cur_field_off + field_len) { + info.rng = { start + cur_field_off, field_len }; + T val; + memcpy(&val, data + info.rng.start, info.rng.len); + display_val(arena, info.desc, desc_fmt, val); + return true; } + cur_field_off += field_len; + return false; } - b8 field_str8(const char *desc_fmt, Display_Fn display_val) const + template + b8 field_be(const char *desc_fmt) const { - u8 str_size = data[start + cur_field_off]; - if (roff < cur_field_off + 1 + str_size) { - info.rng = { start + cur_field_off, 1 + (u64)str_size }; + return field(desc_fmt, hover_display_val_be); + } + + template + b8 field_le(const char *desc_fmt) const + { + return field(desc_fmt, hover_display_val_le); + } + + template + b8 field_str8(const char *desc_fmt, Display_Fn display_val = hover_display_val_be) const + { + // String size can be stored as different types, like u8 (by ROOT I/O) or u32 (by RNTuple). + TStrSize str_size; + memcpy(&str_size, data + start + cur_field_off, sizeof(TStrSize)); + if (roff < cur_field_off + sizeof(TStrSize) + str_size) { + info.rng = { start + cur_field_off, sizeof(TStrSize) + (u64)str_size }; u8 *buf = arena_push_array_nozero(arena, str_size + 1); - memcpy(buf, data + start + cur_field_off + 1, str_size); + memcpy(buf, data + start + cur_field_off + sizeof(TStrSize), str_size); buf[str_size] = 0; String8 s = { buf, str_size }; display_val(arena, info.desc, desc_fmt, s); return true; } - cur_field_off += 1 + str_size; + cur_field_off += sizeof(TStrSize) + str_size; return false; } @@ -769,11 +787,12 @@ struct Try_Sec_Hover_Fn { return false; } - b8 maybe_rootzip(const u8 *data, u64 start) const + b8 maybe_rootzip(b8 *was_zipped = nullptr) const { // TODO boundary checks const u64 range_len = 9; if (display_val_rootzip(arena, info.desc, "Zipped Block", data + start + cur_field_off)) { + if (was_zipped) *was_zipped = true; if (roff < cur_field_off + range_len) { info.rng = { start + cur_field_off, range_len }; hover_display_val_be(arena, info.desc, "", data + start + cur_field_off); @@ -786,51 +805,100 @@ struct Try_Sec_Hover_Fn { info.desc->last_child = info.desc->last_child->prev; } cur_field_off += range_len; + } else if (was_zipped) { + *was_zipped = false; } return false; } b8 tkey() const { - u16 version_be; - memcpy(&version_be, data + start + 4, sizeof(u16)); - u32 version = bswap(version_be); - b8 is_big = version > 1000; + return titled_section("TKey", [this] { + u16 version_be; + memcpy(&version_be, data + start + 4, sizeof(u16)); + u32 version = bswap(version_be); + b8 is_big = version > 1000; - if (is_big) { - return field("NBytes: %u") - || field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { - x = bswap(x); - x -= 1000; - return push_str8_node_child(arena, prev, fmt, x); - }) - || field("Obj Len: %u") - || field("Datetime: ", hover_display_datetime_str) - || field("Key Len: %u") - || field("Cycle: %u") - || field("Seek Key: 0x%" PRIX64) - || field("Seek Pdir: 0x%" PRIX64) - || field("Class Name: %s") - || field("Obj Name: %s") - || field("Obj Title: %s") - ; - } else { - return field("NBytes: %u") - || field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { - x = bswap(x); - return push_str8_node_child(arena, prev, fmt, x); - }) - || field("Obj Len: %u") - || field("Datetime: ", hover_display_datetime_str) - || field("Key Len: %u") - || field("Cycle: %u") - || field("Seek Key: 0x%" PRIX64) - || field("Seek Pdir: 0x%" PRIX64) - || field("Class Name: %s") - || field("Obj Name: %s") - || field("Obj Title: %s") - ; + if (is_big) { + return field_be("NBytes: %u") + || field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { + x = bswap(x); + x -= 1000; + return push_str8_node_child(arena, prev, fmt, x); + }) + || field_be("Obj Len: %u") + || field("Datetime: ", hover_display_datetime_str) + || field_be("Key Len: %u") + || field_be("Cycle: %u") + || field_be("Seek Key: 0x%" PRIX64) + || field_be("Seek Pdir: 0x%" PRIX64) + || field_str8("Class Name: %s") + || field_str8("Obj Name: %s") + || field_str8("Obj Title: %s") + ; + } else { + return field_be("NBytes: %u") + || field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { + x = bswap(x); + return push_str8_node_child(arena, prev, fmt, x); + }) + || field_be("Obj Len: %u") + || field("Datetime: ", hover_display_datetime_str) + || field_be("Key Len: %u") + || field_be("Cycle: %u") + || field_be("Seek Key: 0x%" PRIX64) + || field_be("Seek Pdir: 0x%" PRIX64) + || field_str8("Class Name: %s") + || field_str8("Obj Name: %s") + || field_str8("Obj Title: %s") + ; + } + }); + } + + b8 envelope_preamble() const + { + static const char *const envelope_names[] = { "INVALID", "Header", "Footer", "Page List" }; + return field("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); + }) + || range_data("Envelope size: %s", 6, [] (Arena *arena, String8_Node *prev, const char *fmt, const u8 *payload) { + u64 size; + memcpy(&size, payload, 6); + return push_str8_node_child(arena, prev, fmt, to_pretty_size(arena, size)); + }); + } + + b8 list_frame_preamble() const + { + return titled_section("Frame Preamble", [this] { + return field("Size: %" PRIi64, hover_display_val_le_abs) + || field_le("N Items: %u") + ; + }); + } + + b8 schema_description() const + { + return titled_section("Schema Description", [this] { + return list_frame_preamble(); + }); + } + + template + b8 titled_section(const char *title, F &&fn) const + { + String8_Node *prev_desc = info.desc; + info.desc = push_str8_node_child(arena, prev_desc, title); + + b8 ok = fn(); + + if (!ok) { + pop_str8_node_child(prev_desc, info.desc); + info.desc = prev_desc; } + return ok; } }; @@ -860,29 +928,29 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co u64 start = section.range.start - section.pre_size; u64 roff = off - start; // offset relative to `section` u64 cur_field_off = 0; - Try_Sec_Hover_Fn try_sec_hover { start, roff, data, arena, info, cur_field_off }; + Sec_Hover_Fn hover { start, roff, data, arena, info, cur_field_off }; switch (section.id) { case Sec_RNTuple_Anchor: { - try_sec_hover.tkey() - || try_sec_hover.field("Object len: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { + hover.tkey() + || hover.field("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); }) - || try_sec_hover.field("Class version: %u") - || try_sec_hover.field("Version Epoch: %u") - || try_sec_hover.field("Version Major: %u") - || try_sec_hover.field("Version Minor: %u") - || try_sec_hover.field("Version Patch: %u") - || try_sec_hover.field("Seek Header: 0x%" PRIX64) - || try_sec_hover.field("NBytes Header: %u") - || try_sec_hover.field("Len Header: %u") - || try_sec_hover.field("Seek Footer: 0x%" PRIX64) - || try_sec_hover.field("NBytes Footer: %u") - || try_sec_hover.field("Len Footer: %u") - || try_sec_hover.field("Max Key Size: %u") - || try_sec_hover.field("Checksum: 0x%" PRIX64, hover_display_val_le) + || hover.field_be("Class version: %u") + || hover.field_be("Version Epoch: %u") + || hover.field_be("Version Major: %u") + || hover.field_be("Version Minor: %u") + || hover.field_be("Version Patch: %u") + || hover.field_be("Seek Header: 0x%" PRIX64) + || hover.field_be("NBytes Header: %u") + || hover.field_be("Len Header: %u") + || hover.field_be("Seek Footer: 0x%" PRIX64) + || hover.field_be("NBytes Footer: %u") + || hover.field_be("Len Footer: %u") + || hover.field_be("Max Key Size: %u") + || hover.field_le("Checksum: 0x%" PRIX64) ; } break; @@ -893,46 +961,46 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co b8 is_big = root_version > 1000000; if (is_big) { - try_sec_hover.field("ROOT magic number") - || try_sec_hover.field("ROOT version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { + hover.field_be("ROOT magic number") + || hover.field("ROOT version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { x = bswap(x); x -= 1000000; return push_str8_node_child(arena, prev, fmt, x); }) - || try_sec_hover.field("fBEGIN: 0x%" PRIX64) - || try_sec_hover.field("fEND: 0x%" PRIX64) - || try_sec_hover.field("Seek Free: 0x%" PRIX64) - || try_sec_hover.field("NBytes Free: %u") - || try_sec_hover.field("N Free: %u") - || try_sec_hover.field("NBytes Name: %u") - || try_sec_hover.field("Units: %u") - || try_sec_hover.field("Compression: %u") - || try_sec_hover.field("Seek Info: 0x%" PRIX64) - || try_sec_hover.field("NBytes Info: %u") - || try_sec_hover.range("Padding", section.post_size) + || hover.field_be("fBEGIN: 0x%" PRIX64) + || hover.field_be("fEND: 0x%" PRIX64) + || hover.field_be("Seek Free: 0x%" PRIX64) + || hover.field_be("NBytes Free: %u") + || hover.field_be("N Free: %u") + || hover.field_be("NBytes Name: %u") + || hover.field_be("Units: %u") + || hover.field_be("Compression: %u") + || hover.field_be("Seek Info: 0x%" PRIX64) + || hover.field_be("NBytes Info: %u") + || hover.range("Padding", section.post_size) ; } else { - try_sec_hover.field("ROOT magic number") - || try_sec_hover.field("ROOT version: %u") - || try_sec_hover.field("fBEGIN: 0x%" PRIX64) - || try_sec_hover.field("fEND: 0x%" PRIX64) - || try_sec_hover.field("Seek Free: 0x%" PRIX64) - || try_sec_hover.field("NBytes Free: %u") - || try_sec_hover.field("N Free: %u") - || try_sec_hover.field("NBytes Name: %u") - || try_sec_hover.field("Units: %u") - || try_sec_hover.field("Compression: %u") - || try_sec_hover.field("Seek Info: 0x%" PRIX64) - || try_sec_hover.field("NBytes Info: %u") - || try_sec_hover.range("Padding", section.post_size) + hover.field_be("ROOT magic number") + || hover.field_be("ROOT version: %u") + || hover.field_be("fBEGIN: 0x%" PRIX64) + || hover.field_be("fEND: 0x%" PRIX64) + || hover.field_be("Seek Free: 0x%" PRIX64) + || hover.field_be("NBytes Free: %u") + || hover.field_be("N Free: %u") + || hover.field_be("NBytes Name: %u") + || hover.field_be("Units: %u") + || hover.field_be("Compression: %u") + || hover.field_be("Seek Info: 0x%" PRIX64) + || hover.field_be("NBytes Info: %u") + || hover.range("Padding", section.post_size) ; } } break; case Sec_TFile_Object: { - if (!try_sec_hover.tkey()) { - b8 ok = try_sec_hover.field("File Name: %s") - || try_sec_hover.field("File Title: %s") + if (!hover.tkey()) { + b8 ok = hover.field_str8("File Name: %s") + || hover.field_str8("File Title: %s") ; if (!ok) { u16 version_be; @@ -941,33 +1009,33 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co b8 is_big = version > 1000; if (is_big) { - ok = ok || try_sec_hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { + ok = ok || hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { x = bswap(x); x -= 1000; return push_str8_node_child(arena, prev, fmt, x); }) - || try_sec_hover.field("Created: ", hover_display_datetime_str) - || try_sec_hover.field("Modified: ", hover_display_datetime_str) - || try_sec_hover.field("NBytes Key: %u") - || try_sec_hover.field("NBytes Name: %u") - || try_sec_hover.field("Seek Dir: 0x%" PRIX64) - || try_sec_hover.field("Seek Parent: 0x%" PRIX64) - || try_sec_hover.field("Seek Keys: 0x%" PRIX64) - || try_sec_hover.field("UUID Vers.Class: %u") - || try_sec_hover.field("UUID: %u", hover_display_val_le) + || hover.field("Created: ", hover_display_datetime_str) + || hover.field("Modified: ", hover_display_datetime_str) + || hover.field_be("NBytes Key: %u") + || hover.field_be("NBytes Name: %u") + || hover.field_be("Seek Dir: 0x%" PRIX64) + || hover.field_be("Seek Parent: 0x%" PRIX64) + || hover.field_be("Seek Keys: 0x%" PRIX64) + || hover.field_be("UUID Vers.Class: %u") + || hover.field_le("UUID: %u") ; } else { - ok = ok || try_sec_hover.field("Version: %u") - || try_sec_hover.field("Created: ", hover_display_datetime_str) - || try_sec_hover.field("Modified: ", hover_display_datetime_str) - || try_sec_hover.field("NBytes Key: %u") - || try_sec_hover.field("NBytes Name: %u") - || try_sec_hover.field("Seek Dir: 0x%" PRIX64) - || try_sec_hover.field("Seek Parent: 0x%" PRIX64) - || try_sec_hover.field("Seek Keys: 0x%" PRIX64) - || try_sec_hover.field("UUID Vers.Class: %u") - || try_sec_hover.field("UUID: %u", hover_display_val_le) - || try_sec_hover.range("Padding", 3 * sizeof(u32)) + ok = ok || hover.field_be("Version: %u") + || hover.field("Created: ", hover_display_datetime_str) + || hover.field("Modified: ", hover_display_datetime_str) + || hover.field_be("NBytes Key: %u") + || hover.field_be("NBytes Name: %u") + || hover.field_be("Seek Dir: 0x%" PRIX64) + || hover.field_be("Seek Parent: 0x%" PRIX64) + || hover.field_be("Seek Keys: 0x%" PRIX64) + || hover.field_be("UUID Vers.Class: %u") + || hover.field_le("UUID: %u") + || hover.range("Padding", 3 * sizeof(u32)) ; } } @@ -975,63 +1043,85 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co } break; case Sec_RNTuple_Header: + if (!hover.tkey()) { + b8 zipped; + if (!hover.maybe_rootzip(&zipped)) { + if (zipped) { + hover.range("Payload", section.range.len - section.post_size) + || hover.field_le("Checksum: 0x%" PRIX64) + ; + } else { + hover.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. + || hover.field_le("Flags: 0x%" PRIX64) + || hover.field_str8("Name: %s") + || hover.field_str8("Description: %s") + || hover.field_str8("ROOT version: %s") + || hover.schema_description() + || hover.field_le("Checksum: 0x%" PRIX64) + ; + } + } + } + break; case Sec_RNTuple_Footer: case Sec_Page_List: { - try_sec_hover.tkey() - || try_sec_hover.maybe_rootzip(data, start) - || try_sec_hover.range("Payload", section.range.len - section.post_size) // TODO: improve - || try_sec_hover.field("Checksum: 0x%" PRIX64, hover_display_val_le) + hover.tkey() + || hover.maybe_rootzip() + || hover.range("Payload", section.range.len - section.post_size) // TODO: improve + || hover.field_le("Checksum: 0x%" PRIX64) ; } break; case Sec_Page: { // only try hovering a key if this is the first page of the cluster (<=> pre_size != 0) - b8 ok = section.pre_size && try_sec_hover.tkey(); - ok = ok || try_sec_hover.maybe_rootzip(data, start) - || try_sec_hover.range("Payload", section.range.len - section.post_size) // TODO: improve - || try_sec_hover.field("Checksum: 0x%" PRIX64, hover_display_val_le) + b8 ok = section.pre_size && hover.tkey(); + ok = ok || hover.maybe_rootzip() + || hover.range("Payload", section.range.len - section.post_size) // TODO: improve + || hover.field_le("Checksum: 0x%" PRIX64) ; } break; case Sec_TFile_Info: { - try_sec_hover.tkey() - || try_sec_hover.maybe_rootzip(data, start) - // || try_sec_hover.field("Byte Count: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { + hover.tkey() + || hover.maybe_rootzip() + // || hover.field("Byte Count: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { // x = bswap(x); // x -= 0x400000000; // return push_str8_node_child(arena, prev, fmt, x); // }) - // || try_sec_hover.field("Version: %u") - // || hover_try_object(try_sec_hover) - // || try_sec_hover.field("Name: %u") - // || try_sec_hover.field("N Objects: %u") - || try_sec_hover.range("Payload", section.range.len) // TODO: improve + // || hover.field_be("Version: %u") + // || hover_try_object(hover) + // || hover.field_be("Name: %u") + // || hover.field_be("N Objects: %u") + || hover.range("Payload", section.range.len) // TODO: improve ; } break; case Sec_TFile_FreeList: { - if (!try_sec_hover.tkey()) { + if (!hover.tkey()) { u16 version_be; - memcpy(&version_be, data + start + try_sec_hover.cur_field_off, sizeof(u16)); + memcpy(&version_be, data + start + hover.cur_field_off, sizeof(u16)); u32 version = bswap(version_be); b8 is_big = version > 1000; if (is_big) { - try_sec_hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { + hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { x = bswap(x); x -= 1000; return push_str8_node_child(arena, prev, fmt, x); }) - || try_sec_hover.field("First: 0x%" PRIX64) - || try_sec_hover.field("Last: 0x%" PRIX64) + || hover.field_be("First: 0x%" PRIX64) + || hover.field_be("Last: 0x%" PRIX64) ; } else { - try_sec_hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { + hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { x = bswap(x); return push_str8_node_child(arena, prev, fmt, x); }) - || try_sec_hover.field("First: 0x%X") - || try_sec_hover.field("Last: 0x%X") + || hover.field_be("First: 0x%X") + || hover.field_be("Last: 0x%X") ; } } @@ -1040,6 +1130,5 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co default:; } - return info; } diff --git a/src/str.cpp b/src/str.cpp index c5b4cb0..08a700d 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -78,6 +78,20 @@ String8_Node *push_str8_node_child(Arena *arena, String8_Node *parent, const cha } else { parent->last_child = parent->first_child = snode; } + snode->head = parent->head; return snode; } + +internal +void pop_str8_node_child(String8_Node *parent, String8_Node *node) +{ + if (node->next) + node->next->prev = node->prev; + if (node->prev) + node->prev->next = node->next; + if (node == parent->first_child) + parent->first_child = node->next; + if (node == parent->last_child) + parent->last_child = node->prev; +} diff --git a/src/types.h b/src/types.h index 44f223e..a33ce39 100644 --- a/src/types.h +++ b/src/types.h @@ -26,7 +26,6 @@ using i64 = int64_t; #define LIKELY(x) __builtin_expect(!!(x), 1) #define UNLIKELY(x) __builtin_expect(!!(x), 0) - #ifdef R__BYTESWAP #if defined(R__MACOSX)