diff --git a/src/render.cpp b/src/render.cpp index 99b2929..9341566 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -1,3 +1,19 @@ +internal +std::optional get_section_range(const App_State &app, Section_Id sec) +{ + switch (sec) { + default: return {}; + case Sec_TFile_Header: return Byte_Range { 0, app.tfile_data.root_file_header_size }; + case Sec_TFile_Object: return app.tfile_data.rng_root_file_obj; + case Sec_TFile_Info: return app.tfile_data.rng_root_file_info; + case Sec_TFile_FreeList: return app.tfile_data.rng_root_file_free; + case Sec_TKey_List: return app.rndata.rng_tkeys_list; + case Sec_RNTuple_Anchor: return app.rndata.rng_anchor; + case Sec_RNTuple_Header: return app.rndata.rng_header; + case Sec_RNTuple_Footer: return app.rndata.rng_footer; + } +} + internal void accum_dt_ms(Delta_Time_Accum &accum, f32 dt) { @@ -31,72 +47,62 @@ String8 to_pretty_size(Arena *arena, u64 bytes) } internal -u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data) +Section find_section(App_State &app, u64 off) { - App_State *app = reinterpret_cast(user_data); - const RNTuple_Data &rdata = app->rndata; - const TFile_Data &tdata = app->tfile_data; + const RNTuple_Data &rdata = app.rndata; + const TFile_Data &tdata = app.tfile_data; u64 rblob_sz = rdata.rblob_header_size; // @Incomplete - i64 hilite_cluster = app->viewer.highlight_cluster ? app->viewer.highlighted_cluster : -1; + i64 hilite_cluster = app.viewer.highlight_cluster ? app.viewer.highlighted_cluster : -1; + b8 hilite = false; - off += app->viewer.base_display_addr; - -#define COL(c) (ImColor((c)[0], (c)[1], (c)[2])) // TFile start - if (off <= tdata.root_file_header_size) return COL(app->viewer.col_tfile_header); - if (tdata.rng_root_file_obj.start <= off && off < tdata.rng_root_file_obj.end()) return COL(app->viewer.col_tfile_obj); - if (tdata.rng_root_file_info.start <= off && off < tdata.rng_root_file_info.end()) return COL(app->viewer.col_tfile_info); - if (tdata.rng_root_file_free.start <= off && off < tdata.rng_root_file_free.end()) return COL(app->viewer.col_tfile_free); + if (off <= tdata.root_file_header_size) return { Sec_TFile_Header, { 0, tdata.root_file_header_size }, 0, hilite }; + if (tdata.rng_root_file_obj.start <= off && off < tdata.rng_root_file_obj.end()) return { Sec_TFile_Object, tdata.rng_root_file_obj, 0, hilite }; + if (tdata.rng_root_file_info.start <= off && off < tdata.rng_root_file_info.end()) return { Sec_TFile_Info, tdata.rng_root_file_info, 0, hilite }; + if (tdata.rng_root_file_free.start <= off && off < tdata.rng_root_file_free.end()) return { Sec_TFile_FreeList, tdata.rng_root_file_free, 0, hilite }; - // Handle pages - // fast case: `off` is in the same page info as previous `off`. - if (app->last_pinfo->range.start < off && off < app->last_pinfo->range.end()) { - if (hilite_cluster >= 0 && app->last_pinfo->cluster_id == (u64)hilite_cluster) - return COL(app->viewer.col_highlight); - if (off >= app->last_pinfo->range.end() - app->last_pinfo->checksum_size()) - return COL(app->viewer.col_checksum); - return COL(app->viewer.col_page); + /// Handle pages + { + // fast case: `off` is in the same page info as previous `off`. + if (app.last_pinfo->range.start < off && off < app.last_pinfo->range.end()) { + hilite = hilite_cluster >= 0 && app.last_pinfo->cluster_id == (u64)hilite_cluster; + return { Sec_Page, app.last_pinfo->range, app.last_pinfo->checksum_size(), hilite }; + } + + // still fast case: `off is in the next page info as the previous. + if (app.last_pinfo->next) // don't check if it's checksum, since it's the first byte of the page + app.last_pinfo = app.last_pinfo->next; + if (app.last_pinfo && app.last_pinfo->range.start <= off && off < app.last_pinfo->range.end()) { + hilite = hilite_cluster >= 0 && app.last_pinfo->cluster_id == (u64)hilite_cluster; + return { Sec_Page, app.last_pinfo->range, app.last_pinfo->checksum_size(), hilite }; + } } - // still fast case: `off is in the next page info as the previous. - if (app->last_pinfo->next) // don't check if it's checksum, since it's the first byte of the page - app->last_pinfo = app->last_pinfo->next; + if (rdata.rng_anchor_key.start <= off && off < rdata.rng_anchor.end()) + return { Sec_RNTuple_Anchor, rdata.rng_anchor, 8, hilite }; - if (app->last_pinfo && app->last_pinfo->range.start <= off && off < app->last_pinfo->range.end()) { - if (hilite_cluster >= 0 && app->last_pinfo->cluster_id == (u64)hilite_cluster) - return COL(app->viewer.col_highlight); - if (off == app->last_pinfo->range.start) - return COL(app->viewer.col_page_start); - if (off >= app->last_pinfo->range.end() - app->last_pinfo->checksum_size()) - return COL(app->viewer.col_checksum); - return COL(app->viewer.col_page); - } + if (rdata.rng_header.start - rblob_sz <= off && off < rdata.rng_header.end()) + return { Sec_RNTuple_Header, rdata.rng_header, 8, hilite }; - if (rdata.rng_anchor_key.start <= off && off < rdata.rng_anchor_key.end()) return COL(app->viewer.col_key); - if (rdata.rng_anchor.start <= off && off < rdata.rng_anchor.end() - 8) return COL(app->viewer.col_anchor); - if (rdata.rng_anchor.end() - 8 <= off && off < rdata.rng_anchor.end()) return COL(app->viewer.col_checksum); + if (rdata.rng_footer.start - rblob_sz <= off && off < rdata.rng_footer.end()) + return { Sec_RNTuple_Footer, rdata.rng_footer, 8, hilite }; - if (rdata.rng_header.start - rblob_sz <= off && off < rdata.rng_header.start) return COL(app->viewer.col_key); - if (rdata.rng_header.start <= off && off < rdata.rng_header.end() - 8) return COL(app->viewer.col_header); - if (rdata.rng_header.end() - 8 <= off && off < rdata.rng_header.end()) return COL(app->viewer.col_checksum); - - if (rdata.rng_footer.start - rblob_sz <= off && off < rdata.rng_footer.start) return COL(app->viewer.col_key); - if (rdata.rng_footer.start <= off && off < rdata.rng_footer.end() - 8) return COL(app->viewer.col_footer); - if (rdata.rng_footer.end() - 8 <= off && off < rdata.rng_footer.end()) return COL(app->viewer.col_checksum); - - if (rdata.rng_tkeys_list.start <= off && off < rdata.rng_tkeys_list.end()) return COL(app->viewer.col_tkeys_list); + if (rdata.rng_tkeys_list.start <= off && off < rdata.rng_tkeys_list.end()) + return { Sec_TKey_List, rdata.rng_tkeys_list, 0, hilite }; // @Speed for (u64 cg_idx = 0; cg_idx < rdata.n_cluster_groups; ++cg_idx) { Cluster_Group_Info &cg_info = rdata.cluster_groups[cg_idx]; - if (cg_info.rng_page_list.start -rblob_sz <= off && off < cg_info.rng_page_list.start) return COL(app->viewer.col_page_list); - if (cg_info.rng_page_list.start <= off && off < cg_info.rng_page_list.end() - 8) return COL(app->viewer.col_page_list); - if (cg_info.rng_page_list.end() - 8 <= off && off < cg_info.rng_page_list.end()) return COL(app->viewer.col_checksum); + if (cg_info.rng_page_list.start - rblob_sz <= off && off < cg_info.rng_page_list.end()) + return { Sec_Page_List, cg_info.rng_page_list, 8, hilite }; } // Slow page group lookup, ideally only done once per render when last_pinfo is invalid. for (Page_Info_Chunk *chunk = rdata.page_chunks; chunk; chunk = chunk->next) { - if (chunk->range.start - rblob_sz <= off && off < chunk->range.start) return COL(app->viewer.col_key); + // If we're at the start of a chunk, return a fake Sec_Page used to highlight the RBlob header bytes. + if (chunk->range.start - rblob_sz <= off && off < chunk->range.start) + return { Sec_Page, { chunk->range.start, 0 }, 0, hilite }; + if (chunk->range.start <= off && off < chunk->range.end()) { for (u64 group_idx = chunk->first_group; group_idx < rdata.n_page_groups; ++group_idx) { const Page_Info_Group &group = rdata.page_groups[group_idx]; @@ -105,11 +111,9 @@ u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data) for (Page_Info_Node *pinfo = group.first; pinfo; pinfo = pinfo->next) { if (pinfo->range.start <= off && off < pinfo->range.end()) { - app->last_pinfo = pinfo; - if (hilite_cluster >= 0 && pinfo->cluster_id == (u64)hilite_cluster) return COL(app->viewer.col_highlight); - if (pinfo->range.start == off) return COL(app->viewer.col_page_start); - if (off >= pinfo->range.end() - pinfo->checksum_size()) return COL(app->viewer.col_checksum); - return COL(app->viewer.col_page); + app.last_pinfo = pinfo; + hilite = hilite_cluster >= 0 && pinfo->cluster_id == (u64)hilite_cluster; + return { Sec_Page, pinfo->range, pinfo->checksum_size(), hilite }; } } } @@ -119,9 +123,25 @@ u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data) assert(false); } } -#undef COL - return IM_COL32(0, 0, 0, 0); + return {}; +} + +internal +u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data) +{ + App_State *app = reinterpret_cast(user_data); + off += app->viewer.base_display_addr; + + Section section = find_section(*app, off); + +#define COL(c) (ImColor((c)[0], (c)[1], (c)[2])) + if (section.highlighted) return COL(app->viewer.col_highlight); + if (section.id == Sec_Page && off == section.range.start) return COL(app->viewer.col_page_start); + if (off < section.range.start) return COL(app->viewer.col_key); + if (section.range.end() - section.post_size <= off && off < section.range.end()) return COL(app->viewer.col_checksum); + return COL(app->viewer.col_section[section.id]); +#undef COL } internal @@ -152,21 +172,23 @@ void make_viewer(App_State &app) viewer.mem_edit = make_memory_editor(app); #define COL(c, r, g, b) viewer.c[0] = r/255.0, viewer.c[1] = g/255.0, viewer.c[2] = b/255.0 - COL(col_anchor, 150, 150, 0); - COL(col_header, 150, 0, 50); - COL(col_footer, 50, 0, 150); COL(col_key, 0, 100, 50); - COL(col_tfile_header, 90, 90, 90); - COL(col_tfile_obj, 120, 120, 120); - COL(col_tfile_info, 95, 76, 76); - COL(col_tfile_free, 60, 60, 90); - COL(col_page, 125, 0, 125); COL(col_page_start, 200, 0, 200); COL(col_checksum, 134, 65, 25); - COL(col_page_list, 60, 110, 120); - COL(col_tkeys_list, 100, 140, 100); COL(col_highlight, 190, 190, 190); +#define COL_S(c, r, g, b) viewer.col_section[c][0] = r/255.0, viewer.col_section[c][1] = g/255.0, viewer.col_section[c][2] = b/255.0 + COL_S(Sec_RNTuple_Anchor, 150, 150, 0); + COL_S(Sec_RNTuple_Header, 150, 0, 50); + COL_S(Sec_RNTuple_Footer, 50, 0, 150); + COL_S(Sec_TFile_Header, 90, 90, 90); + COL_S(Sec_TFile_Object, 120, 120, 120); + COL_S(Sec_TFile_Info, 95, 76, 76); + COL_S(Sec_TFile_FreeList, 60, 60, 90); + COL_S(Sec_Page, 125, 0, 125); + COL_S(Sec_Page_List, 60, 110, 120); + COL_S(Sec_TKey_List, 100, 140, 100); #undef COL +#undef COL_S app.viewer = viewer; } @@ -282,65 +304,31 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) ImGui::TableNextColumn(); ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoLabel; - ImGui::ColorEdit3("_TFile Header", app.viewer.col_tfile_header, flags); - ImGui::SameLine(); - if (ImGui::Button("TFile Header")) viewer_jump_to(app.viewer, 0); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.root_file_header_size).c()); + // Unique sections: just display a button that jumps to the start of it and show their size + for (u32 i = 0; i < Sec_COUNT; ++i) { + std::optional range = get_section_range(app, static_cast(i)); + if (!range) continue; - ImGui::ColorEdit3("_TFile Object", app.viewer.col_tfile_obj, flags); - ImGui::SameLine(); - if (ImGui::Button("TFile Object")) viewer_jump_to(app.viewer, app.tfile_data.rng_root_file_obj.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.rng_root_file_obj.len).c()); - - ImGui::ColorEdit3("_TFile Info", app.viewer.col_tfile_info, flags); - ImGui::SameLine(); - if (ImGui::Button("TFile Info")) viewer_jump_to(app.viewer, app.tfile_data.rng_root_file_info.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.rng_root_file_info.len).c()); - - ImGui::ColorEdit3("_TFile FreeList", app.viewer.col_tfile_free, flags); - ImGui::SameLine(); - if (ImGui::Button("TFile FreeList")) viewer_jump_to(app.viewer, app.tfile_data.rng_root_file_free.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.rng_root_file_free.len).c()); - - ImGui::ColorEdit3("_TKey List", app.viewer.col_tkeys_list, flags); - ImGui::SameLine(); - if (ImGui::Button("TKey List")) viewer_jump_to(app.viewer, app.rndata.rng_tkeys_list.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.rng_tkeys_list.len).c()); - - ImGui::ColorEdit3("_RNTuple Anchor", app.viewer.col_anchor, flags); - ImGui::SameLine(); - if (ImGui::Button("RNTuple Anchor")) viewer_jump_to(app.viewer, app.rndata.rng_anchor.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.rng_anchor.len).c()); - - ImGui::ColorEdit3("_RNTuple Header", app.viewer.col_header, flags); - ImGui::SameLine(); - if (ImGui::Button("RNTuple Header")) viewer_jump_to(app.viewer, app.rndata.rng_header.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.rng_header.len).c()); - - ImGui::ColorEdit3("_RNTuple Footer", app.viewer.col_footer, flags); - ImGui::SameLine(); - if (ImGui::Button("RNTuple Footer")) viewer_jump_to(app.viewer, app.rndata.rng_footer.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.rng_footer.len).c()); - - ImGui::ColorEdit3("_TKey Header", app.viewer.col_key, flags); - ImGui::SameLine(); - if (ImGui::Button("TKey Header")) {} // TODO jump to next key + String8 sec_name = section_names[i]; + String8 col_label = push_str8f(scratch.arena, "_%s", sec_name.c()); + ImGui::ColorEdit3(col_label.c(), app.viewer.col_section[i], flags); + ImGui::SameLine(); + if (ImGui::Button(sec_name.c())) + viewer_jump_to(app.viewer, range->start); + ImGui::SameLine(); + ImGui::Text("%s", to_pretty_size(scratch.arena, range->len).c()); + } + // Repeated sections: allow jumping to the N-th ImGui::ColorEdit3("_Page Start", app.viewer.col_page_start, flags); ImGui::SameLine(); - if (ImGui::Button("Page Start")) viewer_jump_to_page(app, 0); + if (ImGui::Button("Page Start")) + viewer_jump_to_page(app, 0); - ImGui::ColorEdit3("_Page", app.viewer.col_page, flags); + ImGui::ColorEdit3("_Page", app.viewer.col_section[Sec_Page], flags); ImGui::SameLine(); - if (ImGui::Button("Page")) viewer_jump_to_page(app, app.viewer.latest_page_gone_to); + if (ImGui::Button("Page")) + viewer_jump_to_page(app, app.viewer.latest_page_gone_to); ImGui::SameLine(); { const i64 step_fast_i64 = app.rndata.n_pages / 100; @@ -357,7 +345,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) ImGui::SameLine(); if (ImGui::Button("Checksum")) {} // TODO jump to next checksum - ImGui::ColorEdit3("_Page List", app.viewer.col_page_list, flags); + ImGui::ColorEdit3("_Page List", app.viewer.col_section[Sec_Page_List], flags); ImGui::SameLine(); if (ImGui::Button("Page List")) viewer_jump_to_page_list(app, app.viewer.latest_page_list_gone_to); ImGui::SameLine(); @@ -387,6 +375,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) i64 cluster_to_highlight = app.viewer.highlighted_cluster; ImGui::PushItemWidth(100.f); if (ImGui::InputScalar("##highlighted_cluster", ImGuiDataType_S64, &cluster_to_highlight, &step_i64, &step_fast_i64, "%u")) { + app.viewer.highlight_cluster = true; viewer_jump_to_cluster(app, cluster_to_highlight); } ImGui::PopItemWidth(); diff --git a/src/render.h b/src/render.h index 2cac229..57421b6 100644 --- a/src/render.h +++ b/src/render.h @@ -1,20 +1,11 @@ struct Viewer { MemoryEditor mem_edit; - f32 col_anchor[3]; - f32 col_header[3]; - f32 col_footer[3]; + f32 col_section[Sec_COUNT][3]; f32 col_key[3]; - f32 col_tfile_header[3]; - f32 col_tfile_obj[3]; - f32 col_tfile_info[3]; - f32 col_tfile_free[3]; - f32 col_page[3]; - f32 col_page_start[3]; f32 col_checksum[3]; - f32 col_page_list[3]; - f32 col_tkeys_list[3]; f32 col_highlight[3]; + f32 col_page_start[3]; u64 base_display_addr; diff --git a/src/rntuple.h b/src/rntuple.h index adcaf8a..fd0362b 100644 --- a/src/rntuple.h +++ b/src/rntuple.h @@ -84,3 +84,42 @@ struct RNTuple_Data { Page_Info_Chunk *page_chunks; u64 n_page_chunks; }; + +// A Section represents an interesting sequence of bytes in the file. +// It is associated to stuff such as a color, some metadata, etc. +enum Section_Id { + Sec_None, + Sec_TFile_Header, + Sec_TFile_Object, + Sec_TFile_Info, + Sec_TFile_FreeList, + Sec_TKey_List, + Sec_RNTuple_Anchor, + Sec_RNTuple_Header, + Sec_RNTuple_Footer, + Sec_Page, + Sec_Page_List, + + Sec_COUNT +}; + +struct Section { + Section_Id id; + Byte_Range range; + u64 post_size; // usually the checksum, included in `range` + b8 highlighted; +}; + +inline const String8 section_names[Sec_COUNT] = { + str8("None"), + str8("TFile Header"), + str8("TFile Object"), + str8("TFile Info"), + str8("TFile FreeList"), + str8("TKey List"), + str8("RNTuple Anchor"), + str8("RNTuple Header"), + str8("RNTuple Footer"), + str8("Page"), + str8("Page List"), +};