From b76187e12d792c82132150b5a072ee19beb77c55 Mon Sep 17 00:00:00 2001 From: silverweed Date: Fri, 2 Aug 2024 09:45:37 +0200 Subject: [PATCH] add hover display rootzip --- src/render.cpp | 67 +++++++++++++++++++++++++++++------- src/render.h | 3 ++ src/rntuple.cpp | 91 +++++++++++++++++++++++++++++++++++++++---------- src/str.cpp | 23 ++++++++++++- src/str.h | 2 +- 5 files changed, 154 insertions(+), 32 deletions(-) diff --git a/src/render.cpp b/src/render.cpp index ae11026..1f12a61 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -67,7 +67,35 @@ ImColor imcol(const f32 col[3], f32 brighten_amt = 0) } internal -u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data) +u32 highlight_zstd_header(const u8 *data, u64 off, App_State &app, f32 brighten) +{ + if (app.viewer.highlight_zstd_headers) { + static const u32 ZSTD_HEADER = 0xFD2FB528; + + if (app.viewer.last_seen_zstd_header_start > 0) { + if (off - app.viewer.last_seen_zstd_header_start <= sizeof(ZSTD_HEADER)) { + return imcol(app.viewer.col_highlight, brighten); + } + app.viewer.last_seen_zstd_header_start = 0; + } + + if (off < app.inspected_file.size - sizeof(ZSTD_HEADER) && data[off] == 0x28) { + u32 bytes; + memcpy(&bytes, data + off, sizeof(ZSTD_HEADER)); + if (bytes == ZSTD_HEADER) { + app.viewer.last_seen_zstd_header_start = off; + return imcol(app.viewer.col_highlight, brighten); + } + } + } else { + app.viewer.last_seen_zstd_header_start = 0; + } + + return 0; +} + +internal +u32 mem_edit_bg_color_fn(const u8 *data, u64 off, void *user_data) { App_State *app = reinterpret_cast(user_data); off += app->base_display_addr; @@ -76,6 +104,10 @@ u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data) if (app->viewer.hovered_range.start <= off && off < app->viewer.hovered_range.end()) brighten = 0.3; + u32 col = highlight_zstd_header(data, off, *app, brighten); + if (app->viewer.last_seen_zstd_header_start) + return col; + i64 hilite_cluster = app->viewer.highlight_cluster ? app->viewer.highlighted_cluster : -1; Section section = find_section(*app, off, hilite_cluster); @@ -186,6 +218,26 @@ void viewer_jump_to_cluster(App_State &app, u64 cluster_idx) viewer_jump_to(app, page->range.start); } +internal +void imgui_render_string_tree(Arena *arena, String8_Node *root, u16 indent = 0) +{ + String8 indent_str; + if (indent) { + indent_str = { arena_push_array_nozero(arena, indent + 1), indent }; + indent_str.str[indent] = 0; + memset(indent_str.str, ' ', indent); + } else { + indent_str = str8(""); + } + + for (String8_Node *node = root; node; node = node->next) { + ImGui::Text("%s%s", indent_str.c(), node->str.c()); + + for (String8_Node *child = node->first_child; child; child = child->next) + imgui_render_string_tree(arena, child, indent + 2); + } +} + internal void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) { @@ -327,6 +379,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) ImGui::SameLine(); ImGui::Checkbox("Highlight cluster", &app.viewer.highlight_cluster); } + ImGui::Checkbox("Highlight ZSTD headers", &app.viewer.highlight_zstd_headers); // --------------------------------- ImGui::Separator(); @@ -335,17 +388,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) { Section hovered_section = find_section(app, app.viewer.hovered_off - 1); Sec_Hover_Info hover_info = get_section_hover_info(scratch.arena, hovered_section, app.viewer.hovered_off - 1, app.inspected_file.mem); - u8 indent = 0; - for (String8_Node *node = hover_info.desc; node; node = node->next) { - String8 fmt = str8("%s"); - if (indent) { - // create indent - fmt = push_str8f(scratch.arena, "%%%ds", indent + node->str.size); - } - - ImGui::Text(fmt.c(), node->str.c()); - indent += 2; - } + imgui_render_string_tree(scratch.arena, hover_info.desc); app.viewer.hovered_range = hover_info.rng; } else { app.viewer.hovered_range = {}; diff --git a/src/render.h b/src/render.h index 93d2f9a..5bbf32a 100644 --- a/src/render.h +++ b/src/render.h @@ -12,6 +12,9 @@ struct Viewer { b8 highlight_cluster; u64 highlighted_cluster; + + b8 highlight_zstd_headers; + u64 last_seen_zstd_header_start; u64 latest_page_gone_to; u64 latest_key_gone_to; diff --git a/src/rntuple.cpp b/src/rntuple.cpp index e83202e..2f13afb 100644 --- a/src/rntuple.cpp +++ b/src/rntuple.cpp @@ -471,6 +471,7 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1) struct Sec_Hover_Info { Byte_Range rng; + // A string tree where children are more indented than parents String8_Node *desc; }; @@ -483,19 +484,19 @@ template String8_Node *hover_display_val_be(Arena *arena, String8_Node *prev, const char *fmt, T val) { val = bswap_if_needed(val); - return push_str8_node(arena, prev, fmt, val); + return push_str8_node_child(arena, prev, fmt, val); } template <> String8_Node *hover_display_val_be(Arena *arena, String8_Node *prev, const char *fmt, String8 val) { - return push_str8_node(arena, prev, fmt, val.c()); + return push_str8_node_child(arena, prev, fmt, val.c()); } template String8_Node *hover_display_val_le(Arena *arena, String8_Node *prev, const char *fmt, T val) { - return push_str8_node(arena, prev, fmt, val); + return push_str8_node_child(arena, prev, fmt, val); } internal @@ -511,7 +512,39 @@ String8_Node *hover_display_datetime_str(Arena *arena, String8_Node *prev, const u32 hour = (datetime & 0x1'ffff) >> 12; u32 min = (datetime & 0xfff) >> 6; u32 sec = datetime & 0x3f; - return push_str8_node(arena, prev, "%s%u/%02u/%02u %02u:%02u:%02u", fmt_pre, year, month, day, hour, min, sec); + return push_str8_node_child(arena, prev, "%s%u/%02u/%02u %02u:%02u:%02u", fmt_pre, year, month, day, hour, min, sec); +} + +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"); + } + if (src[0] == 'C' && src[1] == 'S' && src[2] == Z_DEFLATED) { + zip_method = str8("Old"); + } + if (src[0] == 'X' && src[1] == 'Z' && src[2] == 0) { + zip_method = str8("LZMA"); + } + if (src[0] == 'L' && src[1] == '4') { + zip_method = str8("LZ4"); + } + if (src[0] == 'Z' && src[1] == 'S' && src[2] == 1) { + zip_method = str8("ZSTD"); + } + 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()); + + return sn; } // Functor used by get_section_hover_info to describe the structure of a section and print data about it. @@ -524,9 +557,9 @@ struct Try_Sec_Hover_Fn { u64 &cur_field_off; template - bool field(const char *desc_fmt, - String8_Node *(*display_val)(Arena *, String8_Node *, const char *, TField_Type) = hover_display_val_be - ) const + b8 field(const char *desc_fmt, + String8_Node *(*display_val)(Arena *, String8_Node *, const char *, TField_Type) = hover_display_val_be + ) const { u64 field_len = sizeof(TField_Type); if (roff < cur_field_off + field_len) { @@ -541,9 +574,9 @@ struct Try_Sec_Hover_Fn { } template <> - bool field(const char *desc_fmt, - String8_Node *(*display_val)(Arena *, String8_Node *, const char *, String8) - ) const + b8 field(const char *desc_fmt, + String8_Node *(*display_val)(Arena *, String8_Node *, const char *, String8) + ) const { u8 str_size = data[start + cur_field_off]; if (roff < cur_field_off + 1 + str_size) { @@ -559,11 +592,24 @@ struct Try_Sec_Hover_Fn { return false; } - bool range(const char *desc, u64 range_len) const + b8 range(const char *desc, u64 range_len) const { if (roff < cur_field_off + range_len) { info.rng = { start + cur_field_off, range_len }; - push_str8_node(arena, info.desc, "%s", desc); + push_str8_node_child(arena, info.desc, "%s", desc); + return true; + } + cur_field_off += range_len; + return false; + } + + b8 range_data(const char *desc, u64 range_len, + String8_Node *(*display_val)(Arena *, String8_Node *, const char *, const u8 *) + ) const + { + if (roff < cur_field_off + range_len) { + info.rng = { start + cur_field_off, range_len }; + display_val(arena, info.desc, desc, data + start + cur_field_off); return true; } cur_field_off += range_len; @@ -572,7 +618,7 @@ struct Try_Sec_Hover_Fn { }; internal -bool hover_try_key(const Try_Sec_Hover_Fn &try_sec_hover, const u8 *data, u64 start) +b8 hover_try_key(const Try_Sec_Hover_Fn &try_sec_hover, const u8 *data, u64 start) { u16 version_be; memcpy(&version_be, data + start + 4, sizeof(u16)); @@ -584,7 +630,7 @@ bool hover_try_key(const Try_Sec_Hover_Fn &try_sec_hover, const u8 *data, u64 st || try_sec_hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { x = bswap(x); x -= (x > 1000) * 1000; - return push_str8_node(arena, prev, fmt, x); + return push_str8_node_child(arena, prev, fmt, x); }) || try_sec_hover.field("Obj Len: %u") || try_sec_hover.field("Datetime: ", hover_display_datetime_str) @@ -601,7 +647,7 @@ bool hover_try_key(const Try_Sec_Hover_Fn &try_sec_hover, const u8 *data, u64 st || try_sec_hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { x = bswap(x); x -= (x > 1000) * 1000; - return push_str8_node(arena, prev, fmt, x); + return push_str8_node_child(arena, prev, fmt, x); }) || try_sec_hover.field("Obj Len: %u") || try_sec_hover.field("Datetime: ", hover_display_datetime_str) @@ -616,6 +662,14 @@ bool hover_try_key(const Try_Sec_Hover_Fn &try_sec_hover, const u8 *data, u64 st } } +internal +b8 hover_try_rootzip(const Try_Sec_Hover_Fn &try_sec_hover, const u8 *data, u64 start) +{ + // TODO boundary checks + + return try_sec_hover.range_data("Zipped Block", 9, display_val_rootzip); +} + // `off` is the absolute offset into `data`. internal Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, const u8 *data) @@ -638,7 +692,7 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co || try_sec_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(arena, prev, fmt, x); + return push_str8_node_child(arena, prev, fmt, x); }) || try_sec_hover.field("Class version: %u") || try_sec_hover.field("Version Epoch: %u") @@ -667,7 +721,7 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co || try_sec_hover.field("ROOT version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { x = bswap(x); x -= 1000000; - return push_str8_node(arena, prev, fmt, x); + return push_str8_node_child(arena, prev, fmt, x); }) || try_sec_hover.field("fBEGIN: 0x%lX") || try_sec_hover.field("fEND: 0x%lX") @@ -714,7 +768,7 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co ok = ok || try_sec_hover.field("Version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { x = bswap(x); x -= 1000; - return push_str8_node(arena, prev, fmt, x); + 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) @@ -748,6 +802,7 @@ Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, co case Sec_Page_List: case Sec_Page: { hover_try_key(try_sec_hover, data, start) + || hover_try_rootzip(try_sec_hover, data, start) || try_sec_hover.range("Payload", section.range.len - section.post_size) // TODO: improve || try_sec_hover.field("Checksum: 0x%lX", hover_display_val_le) ; diff --git a/src/str.cpp b/src/str.cpp index 44cef1b..36f45c1 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -38,7 +38,6 @@ String8 to_pretty_size(Arena *arena, u64 bytes) return push_str8f(arena, "%zu B", bytes); } - internal String8_Node *push_str8_node(Arena *arena, String8_Node *prev, const char *fmt, ...) { @@ -54,3 +53,25 @@ String8_Node *push_str8_node(Arena *arena, String8_Node *prev, const char *fmt, return snode; } + +internal +String8_Node *push_str8_node_child(Arena *arena, String8_Node *parent, const char *fmt, ...) +{ + assert(parent); + + String8_Node *snode = arena_push(arena); + + va_list args; + va_start(args, fmt); + snode->str = push_str8fv(arena, fmt, args); + va_end(args); + + if (parent->first_child) { + parent->last_child->next = snode; + parent->last_child = snode; + } else { + parent->last_child = parent->first_child = snode; + } + + return snode; +} diff --git a/src/str.h b/src/str.h index c521518..b8a4145 100644 --- a/src/str.h +++ b/src/str.h @@ -13,6 +13,6 @@ struct String8 { struct String8_Node { String8_Node *next; + String8_Node *first_child, *last_child; String8 str; }; -