diff --git a/src/argparse.cpp b/src/argparse.cpp index ff224c5..404d084 100644 --- a/src/argparse.cpp +++ b/src/argparse.cpp @@ -4,11 +4,12 @@ void print_help(const char *argv0) fprintf(stderr, "rntviewer v" V_MAJOR "." V_MINOR " by silverweed" "\n" - "\nUsage: %s [-t] [-s START] [-l LEN] [-w WIDTH] " + "\nUsage: %s [-t] [-s START] [-l LEN] [-w WIDTH] [-e] " "\n\t-t: no graphics, output to terminal" "\n\t-s: set first displayed byte to START" "\n\t-l: display LEN bytes (only in terminal mode)" "\n\t-w: display WIDTH bytes per column" + "\n\t-e: display some extended info (may slow down the startup)" "\n" , argv0); } @@ -19,6 +20,7 @@ struct Cmdline_Args { u64 start_addr; u64 nbytes_displayed; u16 n_cols; + b8 extended_info; String8 ntpl_name; String8 file_name; @@ -95,15 +97,17 @@ Cmdline_Args parse_args(i32 argc, char **argv) // TODO: refactor this probably for (i32 i = 1; i < argc; ++i) { String8 arg = str8_from_c(argv[i]); - if (arg.str[0] == '-') { + if (arg[0] == '-') { if (arg.size == 2) { - if (arg.str[1] == 't') + if (arg[1] == 't') args.print_to_terminal = true; - else if (arg.str[1] == 's') + else if (arg[1] == 'e') + args.extended_info = true; + else if (arg[1] == 's') parse_int_arg(i, argc, argv, args.start_addr); - else if (arg.str[1] == 'l') + else if (arg[1] == 'l') parse_int_arg(i, argc, argv, args.nbytes_displayed); - else if (arg.str[1] == 'w') { + else if (arg[1] == 'w') { u64 n_cols = 0; parse_int_arg(i, argc, argv, n_cols); args.n_cols = (u16)n_cols; diff --git a/src/render.cpp b/src/render.cpp index da32525..88ef934 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -336,7 +336,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) ImGui::PopItemWidth(); } ImGui::SameLine(); - ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.tot_page_size).c()); + ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.tot_page_comp_size).c()); ImGui::ColorEdit3("_Checksum", app.viewer.col_checksum, flags); ImGui::SameLine(); @@ -368,6 +368,11 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) ImGui::Text("TFile compression: %u", app.tfile_data.compression); ImGui::Text("Num pages: %lu", app.rndata.n_pages); ImGui::Text("Num elements: %lu", app.rndata.n_elems); + if (app.rndata.tot_page_uncomp_size) { + ImGui::Text("Pages uncompressed size: %s (comp.ratio = %.3f)", + to_pretty_size(scratch.arena, app.rndata.tot_page_uncomp_size).c(), + (f32)app.rndata.tot_page_comp_size / app.rndata.tot_page_uncomp_size); + } { const i64 step_fast_i64 = app.rndata.n_clusters / 100; diff --git a/src/render_term.cpp b/src/render_term.cpp index 48a6a50..acb6b4b 100644 --- a/src/render_term.cpp +++ b/src/render_term.cpp @@ -34,92 +34,67 @@ Ansi_Color ansi_color_for_offset(u64 off, App_State &app, const Term_Viewer &vie } internal -String8 render_legend_to_string(Arena *arena, const Term_Viewer &viewer, const App_State &app) +String8_Node *render_legend_to_string(Arena *arena, const Term_Viewer &viewer, const App_State &app, String8_Node *prev) { - Temp scratch = scratch_begin(&arena, 1); - defer { scratch_end(scratch); }; - String8_Node *head = nullptr, *tail = nullptr; String8 color_none = ansi_color_table[ACol_None]; - u64 tot_len = 0; for (u64 section = 1; section < Sec_COUNT; ++section) { Ansi_Color color = viewer.col_section[section]; String8 color_str = ansi_color_table[color]; String8 sec_name = section_names[section]; Byte_Range range = get_section_range(app, static_cast(section)); - String8 s; if (range.len) - s = push_str8f(scratch.arena, "%s%20s %s0x%lX - 0x%lX [%s]\n", + tail = push_str8_node(arena, tail, "%s%20s %s0x%lX - 0x%lX [%s]\n", color_str.c(), sec_name.c(), color_none.c(), - range.start, range.end(), to_pretty_size(scratch.arena, range.len).c()); + range.start, range.end(), to_pretty_size(arena, range.len).c()); else if (section == Sec_Page) - s = push_str8f(scratch.arena, "%s%20s %s(%lu) [%s] \n", + tail = push_str8_node(arena, tail, "%s%20s %s(%lu) [%s] \n", color_str.c(), sec_name.c(), color_none.c(), app.rndata.n_pages, - to_pretty_size(scratch.arena, app.rndata.tot_page_size).c()); + to_pretty_size(arena, app.rndata.tot_page_comp_size).c()); else if (section == Sec_Page_List) - s = push_str8f(scratch.arena, "%s%20s %s(%lu) [%s]\n", + tail = push_str8_node(arena, tail, "%s%20s %s(%lu) [%s]\n", color_str.c(), sec_name.c(), color_none.c(), app.rndata.n_cluster_groups, - to_pretty_size(scratch.arena, app.rndata.tot_page_list_size).c()); - String8_Node *node = arena_push(scratch.arena); - node->str = s; - if (!tail) { - head = node; - } else { - tail->next = node; + to_pretty_size(arena, app.rndata.tot_page_list_size).c()); + if (!head) { + head = tail; + if (prev) + prev->next = head; } - tail = node; - tot_len += s.size; } - { - String8_Node *node = arena_push(scratch.arena); - node->str = push_str8f(scratch.arena, "%s%20s %s\n", ansi_color_table[viewer.col_checksum].c(), "Checksum", color_none); - tail->next = node; - tail = node; - tot_len += node->str.size; - } - { - String8_Node *node = arena_push(scratch.arena); - node->str = push_str8f(scratch.arena, "%s%20s %s\n", ansi_color_table[viewer.col_page_start].c(), "Page Start", color_none); - tail->next = node; - tail = node; - tot_len += node->str.size; - } - { - String8_Node *node = arena_push(scratch.arena); - node->str = push_str8f(scratch.arena, "%s%20s %s\n", ansi_color_table[viewer.col_key].c(), "TKey Header", color_none); - tail->next = node; - tail = node; - tot_len += node->str.size; - } + tail = push_str8_node(arena, tail, "%s%20s %s\n", ansi_color_table[viewer.col_checksum].c(), "Checksum", color_none); + tail = push_str8_node(arena, tail, "%s%20s %s\n", ansi_color_table[viewer.col_page_start].c(), "Page Start", color_none); + tail = push_str8_node(arena, tail, "%s%20s %s\n", ansi_color_table[viewer.col_key].c(), "TKey Header", color_none); - String8 legend { arena_push_array_nozero(arena, tot_len + 1), tot_len + 1 }; - legend.str[tot_len] = 0; - u64 cur_size = 0; - - for (String8_Node *node = head; node; node = node->next) { - memcpy(legend.str + cur_size, node->str.str, node->str.size); - cur_size += node->str.size; - } - - return legend; + return tail; } internal -String8 render_range_to_string(Arena *arena, App_State &app, u64 len, u64 n_cols) +String8_Node *render_info_to_string(Arena *arena, const App_State &app, String8_Node *prev) { - Term_Viewer viewer = make_term_viewer(); + String8_Node *res = push_str8_node(arena, prev, + "\n" + " ROOT version: %u.%u.%u\n" + " TFile compression: %u\n" + " Num pages: %lu\n" + " Num elements: %lu\n", + app.tfile_data.root_version_major, app.tfile_data.root_version_minor, app.tfile_data.root_version_patch, + app.tfile_data.compression, + app.rndata.n_pages, + app.rndata.n_elems); - String8 legend = render_legend_to_string(arena, viewer, app); + if (app.rndata.tot_page_uncomp_size) { + res = push_str8_node(arena, res, " Pages uncompressed size: %s (comp.ratio = %.3f)\n", + to_pretty_size(arena, app.rndata.tot_page_uncomp_size).c(), + (f32)app.rndata.tot_page_comp_size / app.rndata.tot_page_uncomp_size); + } - // scratch must be created after `render_legend_to_string` - Temp scratch = scratch_begin(&arena, 1); - defer { scratch_end(scratch); }; - - String8 ntpl_desc = rntuple_description(scratch.arena, app.rndata); - String8 header_str = push_str8f(scratch.arena, "RNTuple '%s' (%s) from file \"%s\"", - app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c()); + return res; +} +internal +String8 render_range_bytes_to_string(Arena *arena, Arena *scratch_arena, App_State &app, Term_Viewer &viewer, u64 len, u64 n_cols) +{ if (n_cols == 0) n_cols = 32; u64 n_lines = len / n_cols; @@ -132,19 +107,15 @@ String8 render_range_to_string(Arena *arena, App_State &app, u64 len, u64 n_cols u64 max_addr_len = 1 + ceilf(logf(max_addr + 1) * RECP_LOG_16); const u64 n_spaces_after_line_addr = 3; u64 line_addr_size = (max_addr_len + n_spaces_after_line_addr) * n_lines; - String8 line_addr_fmt = push_str8f(scratch.arena, "%%0%dlX:", max_addr_len - 1); + String8 line_addr_fmt = push_str8f(scratch_arena, "%%0%dlX:", max_addr_len - 1); // NOTE: +3 because we need 2 chars for each byte + 1 whitespace. - u64 buf_size = (ACOL_MAX_LEN + 3) * len; + u64 buf_size = (ACOL_MAX_LEN + 3) * len; // at most one color per byte buf_size += n_lines; // one newline per line buf_size += n_lines * ACOL_MAX_LEN; // reset color every line - buf_size += line_addr_size; + buf_size += line_addr_size; // address column buf_size += 1; // initial newline - buf_size += header_str.size; - buf_size += 2; // two newlines after header - buf_size += 2; // two newlines before legend - buf_size += legend.size; - buf_size += 1; // trailing zero + buf_size += 3; // final newlines + trailing zero char *buf = arena_push_array_nozero(arena, buf_size); buf[len] = 0; @@ -160,12 +131,6 @@ String8 render_range_to_string(Arena *arena, App_State &app, u64 len, u64 n_cols char *cur = buf; *cur++ = '\n'; - // header - memcpy(cur, header_str.str, header_str.size); - cur += header_str.size; - *cur++ = '\n'; - *cur++ = '\n'; - // first line addr cur += sprintf(cur, line_addr_fmt.c(), start); for (u32 i = 0; i < n_spaces_after_line_addr; ++i) @@ -200,11 +165,36 @@ String8 render_range_to_string(Arena *arena, App_State &app, u64 len, u64 n_cols *cur++ = '\n'; *cur++ = '\n'; - memcpy(cur, legend.str, legend.size); - cur += legend.size; + u64 real_buf_size = cur - buf; + assert(real_buf_size <= buf_size); + *cur = 0; - *cur++ = 0; - u64 len_used = (u64)(cur - buf); - - return { (u8 *)buf, len_used }; + return { (u8 *)buf, real_buf_size }; +} + +internal +String8_Node *render_range_to_string(Arena *arena, App_State &app, u64 len, u64 n_cols) +{ + Term_Viewer viewer = make_term_viewer(); + + Temp scratch = scratch_begin(&arena, 1); + defer { scratch_end(scratch); }; + + // Header + String8 ntpl_desc = rntuple_description(scratch.arena, app.rndata); + String8_Node *result = push_str8_node(arena, nullptr, "RNTuple '%s' (%s) from file \"%s\"\n", + app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c()); + String8_Node *result_tail = result; + + // Bytes view + String8_Node *bytes_view = arena_push(arena); + bytes_view->str = render_range_bytes_to_string(arena, scratch.arena, app, viewer, len, n_cols); + result_tail->next = bytes_view; + result_tail = bytes_view; + + // Footer + result_tail = render_legend_to_string(arena, viewer, app, result_tail); + result_tail = render_info_to_string(arena, app, result_tail); + + return result; } diff --git a/src/rntuple.cpp b/src/rntuple.cpp index 695d0e9..50075cc 100644 --- a/src/rntuple.cpp +++ b/src/rntuple.cpp @@ -88,7 +88,7 @@ void gather_ntuple_metadata(Arena *arena, RMicroFileReader &reader, const RNTupl u64 n_pages = 0; u64 n_elems = 0; - u64 tot_page_size = 0; + u64 tot_page_comp_size = 0; Page_Info_Node *pinfo_head = nullptr, *pinfo_tail = nullptr; Page_Info_Node *last_inserted_pinfo = nullptr; @@ -186,7 +186,7 @@ void gather_ntuple_metadata(Arena *arena, RMicroFileReader &reader, const RNTupl last_inserted_pinfo = pinfo; ++n_pages; - tot_page_size += pinfo->range.len; + tot_page_comp_size += pinfo->range.len; n_elems += page_info.fNElements; } } @@ -270,7 +270,7 @@ void gather_ntuple_metadata(Arena *arena, RMicroFileReader &reader, const RNTupl rndata.n_page_chunks = n_chunks; rndata.n_pages = n_pages; rndata.n_elems = n_elems; - rndata.tot_page_size = tot_page_size; + rndata.tot_page_comp_size = tot_page_comp_size; rndata.cluster_groups = cluster_groups; rndata.n_cluster_groups = cg_idx; rndata.tot_page_list_size = tot_page_list_size; @@ -346,7 +346,20 @@ TFile_Data get_tfile_data(const Inspected_File &file, String8 ntpl_name) } internal -RNTuple_Data get_rntuple_data(Arena *arena, const Inspected_File &file, String8 ntpl_name) +u64 calc_page_uncomp_size(const u8 *fmem, const Page_Info_Node *page_head) +{ + TIMED_SCOPE(); + u64 tot_size = 0; + for (const Page_Info_Node *pinfo = page_head; pinfo; pinfo = pinfo->next) { + const u8 *src = fmem + pinfo->range.start; + u32 uncomp_size = src[6] | (src[7] << 8) | (src[8] << 16); + tot_size += uncomp_size; + } + return tot_size; +} + +internal +RNTuple_Data get_rntuple_data(Arena *arena, const Inspected_File &file, String8 ntpl_name, b8 extended_info) { RNTuple_Data rndata {}; @@ -371,6 +384,8 @@ RNTuple_Data get_rntuple_data(Arena *arena, const Inspected_File &file, String8 rndata.rng_tkeys_list.len = file_info.tkeys_list_nbytes; gather_ntuple_metadata(arena, file_reader, file_info, rndata); + if (extended_info) + rndata.tot_page_uncomp_size = calc_page_uncomp_size(file.mem, rndata.pages); } return rndata; @@ -613,6 +628,7 @@ String8_Node *display_val_rootzip(Arena *arena, String8_Node *prev, const char * 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()); + sn = push_str8_node(arena, sn, "Comp. ratio: %.2f", (f32)comp_size / uncomp_size); return sn; } diff --git a/src/rntuple.h b/src/rntuple.h index 3d00998..04538c7 100644 --- a/src/rntuple.h +++ b/src/rntuple.h @@ -74,7 +74,8 @@ struct RNTuple_Data { u64 n_pages; // total number of elements of all pages u64 n_elems; - u64 tot_page_size; + u64 tot_page_comp_size; + u64 tot_page_uncomp_size; // TODO Range_Seq *checksums; diff --git a/src/rntviewer.cpp b/src/rntviewer.cpp index d4ad3b8..e430c15 100644 --- a/src/rntviewer.cpp +++ b/src/rntviewer.cpp @@ -134,14 +134,17 @@ int main(int argc, char **argv) app.ntpl_name = args.ntpl_name; app.base_display_addr = args.start_addr; app.tfile_data = get_tfile_data(app.inspected_file, app.ntpl_name); - app.rndata = get_rntuple_data(arena, app.inspected_file, app.ntpl_name); + app.rndata = get_rntuple_data(arena, app.inspected_file, app.ntpl_name, args.extended_info); if (args.print_to_terminal) { u64 nbytes_displayed = args.nbytes_displayed; if (!nbytes_displayed) nbytes_displayed = 1000; - String8 rendered = render_range_to_string(arena, app, nbytes_displayed, args.n_cols); - printf("%s\n", rendered.c()); + String8_Node *rendered = render_range_to_string(arena, app, nbytes_displayed, args.n_cols); + printf("\n"); + for (String8_Node *s = rendered; s; s = s->next) + printf("%s", s->str.c()); + printf("\n"); return 0; }