diff --git a/src/app_state.h b/src/app_state.h index 1bebb20..d194b45 100644 --- a/src/app_state.h +++ b/src/app_state.h @@ -36,7 +36,7 @@ struct App_State { // Cache the last info node selected for faster lookup of offsets const Page_Info_Node *last_pinfo; - const Other_Root_Obj_Info *last_other_root_obj; + const Section *last_other_root_obj; }; internal diff --git a/src/hover.cpp b/src/hover.cpp index ff8426c..77044d1 100644 --- a/src/hover.cpp +++ b/src/hover.cpp @@ -901,7 +901,6 @@ struct Sec_Hover_Fn { u32 version = bswap(version_be); b8 is_big = version > 1000; - // FIXME: this is not correct in all cases 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) { @@ -1069,8 +1068,8 @@ struct Sec_Hover_Fn { void other_root_obj() { - Other_Root_Obj_Info *info = (Other_Root_Obj_Info *)section.info; - String8 name = (info && info->class_name.size) ? info->class_name : str8("(Unknown)"); + String8 *class_name = (String8 *)section.info; + String8 name = class_name->size ? *class_name : str8("(Unknown)"); titled_section(name.c(), [this] { tkey(); range("Payload", section.range.len - section.post_size); diff --git a/src/render.cpp b/src/render.cpp index 2b3f547..ae06f45 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -220,54 +220,6 @@ Page_Info_Node *viewer_jump_to_page(App_State &app, u64 page_idx) return page; } -internal -void viewer_jump_to_page_list(App_State &app, u64 page_list_idx) -{ - if (app.rndata.n_cluster_groups == 0) - return; - page_list_idx = (page_list_idx + app.rndata.n_cluster_groups) % app.rndata.n_cluster_groups; - - Cluster_Group_Info &cg_info = app.rndata.cluster_groups[page_list_idx]; - - app.viewer.latest_section_gone_to[Sec_Page_List] = page_list_idx; - viewer_jump_to(app, cg_info.rng_page_list.start); -} - -internal -void viewer_jump_to_free_slot(App_State &app, u64 free_slot_idx) -{ - u64 n_free_slots = app.tfile_data.sections[Sec_Free_Slot].count; - assert(n_free_slots > 0); - - u64 idx = (free_slot_idx + n_free_slots) % n_free_slots; - - Section *sec = app.tfile_data.sections[Sec_Free_Slot].head; - for (u64 i = 0; i < idx; ++i) { - sec = sec->next; - assert(sec); - } - - app.viewer.latest_section_gone_to[Sec_Free_Slot] = idx; - viewer_jump_to(app, sec->range.start); -} - -internal -void viewer_jump_to_other_root_obj(App_State &app, u64 obj_idx) -{ - u64 n_other_root_objs = app.tfile_data.tkeys_data.n_other_root_obj; - if (n_other_root_objs == 0) - return; - - obj_idx = (obj_idx + n_other_root_objs) % n_other_root_objs; - - Other_Root_Obj_Info *info = app.tfile_data.tkeys_data.other_root_obj; - for (u64 i = 0; i < obj_idx; ++i) - info = info->next; - - app.viewer.latest_section_gone_to[Sec_Other] = obj_idx; - viewer_jump_to(app, info->section.range.start); -} - internal void viewer_jump_to_cluster(App_State &app, u64 cluster_idx) { @@ -284,6 +236,45 @@ void viewer_jump_to_cluster(App_State &app, u64 cluster_idx) viewer_jump_to(app, page->range.start); } +internal +void viewer_jump_to_page_list(App_State &app, u64 page_list_idx) +{ + if (app.rndata.n_cluster_groups == 0) + return; + + page_list_idx = (page_list_idx + app.rndata.n_cluster_groups) % app.rndata.n_cluster_groups; + + Cluster_Group_Info &cg_info = app.rndata.cluster_groups[page_list_idx]; + + app.viewer.latest_section_gone_to[Sec_Page_List] = page_list_idx; + viewer_jump_to(app, cg_info.rng_page_list.start); +} + +internal +void viewer_jump_to_section(App_State &app, Section_Id id, u64 sec_idx) +{ + // special cases + if (id == Sec_Page_List) { + viewer_jump_to_page_list(app, sec_idx); + return; + } + + const Sections §ions = app.tfile_data.sections[id]; + if (sections.count == 0) + return; + + Section *sec = sections.head; + assert(sec); + + sec_idx = (sec_idx + sections.count) % sections.count; + + for (u64 i = 0; i < sec_idx; ++i) { + sec = sec->next; + assert(sec); + } + viewer_jump_to(app, sec->range.start); +} + internal void imgui_render_string_tree(Arena *arena, String8_Node *root, String8_Node *highlighted, u16 indent = 0) { @@ -381,7 +372,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) assert(app.base_display_addr < app.inspected_file.size); void *content = app.inspected_file.mem + app.base_display_addr; app.last_pinfo = &invalid_pinfo; - app.last_other_root_obj = &invalid_other_root_obj_info; + app.last_other_root_obj = &invalid_section; app.viewer.mem_edit.DrawContents(content, content_size, app.base_display_addr); ImGui::TableNextColumn(); @@ -391,19 +382,34 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) // ------------------------------- // 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) { - u32 sec_idx = 0; - for (Section *sec = app.tfile_data.sections[i].head; sec; sec = sec->next, ++sec_idx) { - if (!sec->range.len) continue; + for (u32 i = 1; i < Sec_COUNT; ++i) { + if (!app.tfile_data.sections[i].head) + continue; - 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], edit_flags); + // TODO: handle pages like other sections + if (i == Sec_Page) + continue; + + 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], edit_flags); + ImGui::SameLine(); + if (ImGui::Button(sec_name.c())) + viewer_jump_to_section(app, static_cast<Section_Id>(i), app.viewer.latest_section_gone_to[i]); + ImGui::SameLine(); + ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.sections[i].tot_size).c()); + + if (app.tfile_data.sections[i].count > 1) { ImGui::SameLine(); - if (ImGui::Button(push_str8f(scratch.arena, "%s_%u", sec_name.c(), sec_idx).c())) - viewer_jump_to(app, sec->range.start); - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, sec->range.len).c()); + i64 sec_to_go_to = app.viewer.latest_section_gone_to[i]; + ImGui::PushItemWidth(80.f); + if (ImGui::InputScalar(push_str8f(scratch.arena, "##%s_viewed", sec_name.c()).c(), ImGuiDataType_S64, + &sec_to_go_to, &step_i64, nullptr, "%u") + && ImGui::IsItemDeactivatedAfterEdit()) + { + viewer_jump_to_section(app, static_cast<Section_Id>(i), sec_to_go_to); + } + ImGui::PopItemWidth(); } } @@ -446,62 +452,6 @@ 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_section[Sec_Page_List], edit_flags); - ImGui::SameLine(); - if (ImGui::Button("Page List")) - viewer_jump_to_page_list(app, app.viewer.latest_section_gone_to[Sec_Page_List]); - - ImGui::SameLine(); - { - i64 page_list_to_go_to = app.viewer.latest_section_gone_to[Sec_Page_List]; - ImGui::PushItemWidth(80.f); - if (ImGui::InputScalar("##page_list_viewed", ImGuiDataType_S64, &page_list_to_go_to, &step_i64, nullptr, "%u") - && ImGui::IsItemDeactivatedAfterEdit()) - { - viewer_jump_to_page_list(app, page_list_to_go_to); - } - ImGui::PopItemWidth(); - } - ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.tot_page_list_size).c()); - - if (app.tfile_data.tkeys_data.n_other_root_obj > 0) { - ImGui::ColorEdit3("_Other", app.viewer.col_section[Sec_Other], edit_flags); - ImGui::SameLine(); - if (ImGui::Button("Other")) - viewer_jump_to_other_root_obj(app, app.viewer.latest_section_gone_to[Sec_Other]); - - ImGui::SameLine(); - { - i64 other_root_obj_to_go_to = app.viewer.latest_section_gone_to[Sec_Other]; - ImGui::PushItemWidth(80.f); - if (ImGui::InputScalar("##other_root_objviewed", ImGuiDataType_S64, &other_root_obj_to_go_to, &step_i64, nullptr, "%u") - && ImGui::IsItemDeactivatedAfterEdit()) - { - viewer_jump_to_other_root_obj(app, other_root_obj_to_go_to); - } - ImGui::PopItemWidth(); - } - } - - if (app.tfile_data.sections[Sec_Free_Slot].count) { - ImGui::ColorEdit3("_Free Slot", app.viewer.col_section[Sec_Free_Slot], edit_flags); - ImGui::SameLine(); - if (ImGui::Button("Free Slot")) - viewer_jump_to_free_slot(app, app.viewer.latest_section_gone_to[Sec_Free_Slot]); - ImGui::SameLine(); - { - i64 free_slot_to_go_to = app.viewer.latest_section_gone_to[Sec_Free_Slot]; - ImGui::PushItemWidth(80.f); - if (ImGui::InputScalar("##free_slot_viewed", ImGuiDataType_S64, &free_slot_to_go_to, &step_i64, nullptr, "%u") - && ImGui::IsItemDeactivatedAfterEdit()) - { - viewer_jump_to_free_slot(app, free_slot_to_go_to); - } - ImGui::PopItemWidth(); - } - } - // Misc information // ------------------------------- ImGui::Separator(); diff --git a/src/render_term.cpp b/src/render_term.cpp index aea77cc..006ca40 100644 --- a/src/render_term.cpp +++ b/src/render_term.cpp @@ -141,7 +141,7 @@ String8 render_range_bytes_to_string(Arena *arena, App_State &app, Term_Viewer & // We need to properly initialize this before calling mem_edit_bg_color_fn! app.last_pinfo = &invalid_pinfo; - app.last_other_root_obj = &invalid_other_root_obj_info; + app.last_other_root_obj = &invalid_section; const u8 *data = app.inspected_file.mem; assert(range.start <= max_addr); diff --git a/src/rntuple.cpp b/src/rntuple.cpp index 8c06040..4cfe89f 100644 --- a/src/rntuple.cpp +++ b/src/rntuple.cpp @@ -8,6 +8,17 @@ Section *push_section(Arena *arena, TFile_Data &tdata, Section_Id id) return sec; } +internal +void compute_tot_sections_size(Sections *sections) +{ + for (u64 i = 0; i < Sec_COUNT; ++i) { + u64 tot_size = 0; + for (Section *sec = sections[i].head; sec; sec = sec->next) + tot_size += sec->range.len; + sections[i].tot_size = tot_size; + } +} + internal b8 rntuple_is_old_version(const ROOT::RNTuple &ntuple) { @@ -475,7 +486,6 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1) { // These are supposed to never be null. If they are invalid, they must be set to `&invalid_pinfo` etc. assert(app.last_pinfo); - assert(app.last_other_root_obj); const RNTuple_Data &rdata = app.rndata; @@ -491,8 +501,6 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1) u64 rblob_sz = app.tfile_data.sections[Sec_Page].head->pre_size; - const TKeys_Data &tdata = app.tfile_data.tkeys_data; - /// Page fast lookup (relative to app.last_pinfo) { // fast case: `off` is in the same page info as previous `off`. @@ -567,10 +575,10 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1) } // Copypasted from the fast page lookup - if (tdata.n_other_root_obj > 0) { - // fast case: `off` is in the same page info as previous `off`. + if (app.tfile_data.sections[Sec_Other].count > 0) { + // fast case: `off` is in the same obj as previous `off`. for (i32 i = 0; i < 2; ++i) { - const Section &other_sec = app.last_other_root_obj->section; + const Section &other_sec = *app.last_other_root_obj; if (other_sec.range.start - other_sec.pre_size < off && off < other_sec.range.end()) { return other_sec; } @@ -582,10 +590,10 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1) } // Slow linear lookup, ideally only done once per render when last_other_root_obj is invalid. - for (Other_Root_Obj_Info *other = tdata.other_root_obj; other; other = other->next) { - if (other->section.range.start - other->section.pre_size <= off && off < other->section.range.end()) { - app.last_other_root_obj = other; - return other->section; + for (Section *other_sec = app.tfile_data.sections[Sec_Other].head; other_sec; other_sec = other_sec->next) { + if (other_sec->range.start - other_sec->pre_size <= off && off < other_sec->range.end()) { + app.last_other_root_obj = other_sec; + return *other_sec; } } } diff --git a/src/rntuple.h b/src/rntuple.h index cb2b0e9..535d50b 100644 --- a/src/rntuple.h +++ b/src/rntuple.h @@ -3,12 +3,6 @@ struct Byte_Range { inline u64 end() const { return start + len; } }; -// Used to store location information about stuff like checksums, page lists, etc -struct Range_Seq { - Range_Seq *next; - Byte_Range range; -}; - struct Page_Info_Node { Page_Info_Node *next; Page_Info_Node *prev; @@ -32,11 +26,16 @@ struct Page_Info_Node { static const Page_Info_Node invalid_pinfo {}; +// A page group is a grouping of a fixed number of page infos, whose range is equal to the combined ranges +// of its components. It is an acceleration structure used to more quickly find the correct page info +// that an offset belongs to. struct Page_Info_Group { Byte_Range range; Page_Info_Node *first; }; +// A page chunk is a grouping of adjacent pages, used to quickly determine if an offset is part +// of a page or not. struct Page_Info_Chunk { Page_Info_Chunk *next; Byte_Range range; @@ -62,11 +61,11 @@ enum Section_Id { Sec_TFile_Info, Sec_TFile_FreeList, Sec_TKey_List, + Sec_RNTuple_Anchor, Sec_RNTuple_Header, Sec_RNTuple_Footer, Sec_Page_List, Sec_Page, - Sec_RNTuple_Anchor, Sec_Free_Slot, // any other object stored in a TKey Sec_Other, @@ -84,9 +83,13 @@ struct Section { b8 highlighted; // Optional pointer to the specific information for the section. - // e.g. if the section is a Page this points to the relative Page_Info_Node + // Here are the actual types: + // - Sec_RNTuple_Anchor => ROOT::RNTuple + // - Sec_Page => Page_Info_Node (aliased from RNTuple_Data::pages) + // - Sec_Other => String8 (the class name of the object) const void *info; }; +static const Section invalid_section {}; // @Volatile with Section_Id internal const String8 section_names[] = { @@ -96,11 +99,11 @@ internal const String8 section_names[] = { str8("TFile Streamer Info"), str8("TFile FreeList"), str8("TKey List"), + str8("RNTuple Anchor"), str8("RNTuple Header"), str8("RNTuple Footer"), str8("Page List"), str8("Page"), - str8("RNTuple Anchor"), str8("Free Slot"), str8("Other"), }; @@ -117,24 +120,15 @@ struct RNTuple_Anchor_Info { u64 offset_in_file; }; -struct Other_Root_Obj_Info { - Other_Root_Obj_Info *next; - String8 class_name; - Section section; -}; -static const Other_Root_Obj_Info invalid_other_root_obj_info {}; - struct Sections { Section *head, *tail; u32 count; + u64 tot_size; }; struct TKeys_Data { RNTuple_Anchor_Info *rntuples; Byte_Range_Node *rblob_keys; - - Other_Root_Obj_Info *other_root_obj; - u64 n_other_root_obj; }; struct TFile_Data { diff --git a/src/rntviewer.cpp b/src/rntviewer.cpp index 3feb5a6..4d23096 100644 --- a/src/rntviewer.cpp +++ b/src/rntviewer.cpp @@ -176,6 +176,8 @@ int main(int argc, char **argv) // else if (success) app.rndata = get_rntuple_data(arena, app.inspected_file, app.tfile_data, args.extended_info); + compute_tot_sections_size(app.tfile_data.sections); + if (args.print_to_terminal) { u64 nbytes_displayed = args.nbytes_displayed; if (!nbytes_displayed) diff --git a/src/tfile.cpp b/src/tfile.cpp index 9d6ff61..2a49933 100644 --- a/src/tfile.cpp +++ b/src/tfile.cpp @@ -201,7 +201,6 @@ b8 walk_tkeys(Arena *arena, const u8 *data, u64 data_len, u32 flags, TFile_Data cur = pre + tfile_obj_nbytes; RNTuple_Anchor_Info *rntuple_info_tail = nullptr; - Other_Root_Obj_Info *other_root_obj_tail = nullptr; Sections *sections = tfile_data.sections; @@ -322,17 +321,14 @@ b8 walk_tkeys(Arena *arena, const u8 *data, u64 data_len, u32 flags, TFile_Data tkeys_data.rblob_keys = rblob_key; } else if (flags & WTK_COLLECT_OTHER_ROOT_OBJS) { - Other_Root_Obj_Info *oth_info = arena_push<Other_Root_Obj_Info>(arena); - if (cname_len) { - oth_info->class_name = str8_from_buf(arena, data + cname_off + 1, cname_len); - } - oth_info->section.id = Sec_Other; - oth_info->section.info = oth_info; - oth_info->section.range.start = cur + keylen; - oth_info->section.range.len = n_bytes - keylen; - oth_info->section.pre_size = keylen; - push_to_sll(tkeys_data.other_root_obj, other_root_obj_tail, oth_info); - tkeys_data.n_other_root_obj++; + Section *sec_other = push_section(arena, tfile_data, Sec_Other); + String8 *class_name = arena_push<String8>(arena); + sec_other->info = class_name; + if (cname_len) + *class_name = str8_from_buf(arena, data + cname_off + 1, cname_len); + sec_other->range.start = cur + keylen; + sec_other->range.len = n_bytes - keylen; + sec_other->pre_size = keylen; } } cur += n_bytes;