mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-08 21:17:59 +00:00
Refactor data diffing & expose WASM API (#256)
* Refactor data diffing & expose WASM API * Update test snapshots
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
use std::{cmp::min, default::Default, mem::take};
|
||||
use std::default::Default;
|
||||
|
||||
use egui::{Label, Sense, Widget, text::LayoutJob};
|
||||
use objdiff_core::{
|
||||
diff::{
|
||||
DataDiff, DataDiffKind, DataRelocationDiff,
|
||||
data::resolve_relocation,
|
||||
display::{ContextItem, HoverItem, HoverItemColor, relocation_context, relocation_hover},
|
||||
DataDiffKind, DataDiffRow,
|
||||
data::BYTES_PER_ROW,
|
||||
display::{data_row_context, data_row_hover},
|
||||
},
|
||||
obj::Object,
|
||||
};
|
||||
@@ -13,84 +13,30 @@ use objdiff_core::{
|
||||
use super::diff::{context_menu_items_ui, hover_items_ui};
|
||||
use crate::views::{appearance::Appearance, write_text};
|
||||
|
||||
pub(crate) const BYTES_PER_ROW: usize = 16;
|
||||
|
||||
fn data_row_hover(obj: &Object, diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) -> Vec<HoverItem> {
|
||||
let mut out = Vec::new();
|
||||
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||
let mut prev_reloc = None;
|
||||
let mut first = true;
|
||||
for reloc_diff in reloc_diffs {
|
||||
let reloc = &reloc_diff.reloc;
|
||||
if prev_reloc == Some(reloc) {
|
||||
// Avoid showing consecutive duplicate relocations.
|
||||
// We do this because a single relocation can span across multiple diffs if the
|
||||
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||
continue;
|
||||
}
|
||||
prev_reloc = Some(reloc);
|
||||
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
out.push(HoverItem::Separator);
|
||||
}
|
||||
|
||||
let color = get_hover_item_color_for_diff_kind(reloc_diff.kind);
|
||||
|
||||
let reloc = resolve_relocation(&obj.symbols, reloc);
|
||||
out.append(&mut relocation_hover(obj, reloc, Some(color)));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn data_row_context(
|
||||
obj: &Object,
|
||||
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||
) -> Vec<ContextItem> {
|
||||
let mut out = Vec::new();
|
||||
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||
let mut prev_reloc = None;
|
||||
for reloc_diff in reloc_diffs {
|
||||
let reloc = &reloc_diff.reloc;
|
||||
if prev_reloc == Some(reloc) {
|
||||
// Avoid showing consecutive duplicate relocations.
|
||||
// We do this because a single relocation can span across multiple diffs if the
|
||||
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||
continue;
|
||||
}
|
||||
prev_reloc = Some(reloc);
|
||||
|
||||
let reloc = resolve_relocation(&obj.symbols, reloc);
|
||||
out.append(&mut relocation_context(obj, reloc, None));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn data_row_hover_ui(
|
||||
ui: &mut egui::Ui,
|
||||
obj: &Object,
|
||||
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||
diff_row: &DataDiffRow,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
hover_items_ui(ui, data_row_hover(obj, diffs), appearance);
|
||||
hover_items_ui(ui, data_row_hover(obj, diff_row), appearance);
|
||||
});
|
||||
}
|
||||
|
||||
fn data_row_context_menu(
|
||||
ui: &mut egui::Ui,
|
||||
obj: &Object,
|
||||
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||
diff_row: &DataDiffRow,
|
||||
column: usize,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
context_menu_items_ui(ui, data_row_context(obj, diffs), column, appearance);
|
||||
context_menu_items_ui(ui, data_row_context(obj, diff_row), column, appearance);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -103,27 +49,18 @@ fn get_color_for_diff_kind(diff_kind: DataDiffKind, appearance: &Appearance) ->
|
||||
}
|
||||
}
|
||||
|
||||
fn get_hover_item_color_for_diff_kind(diff_kind: DataDiffKind) -> HoverItemColor {
|
||||
match diff_kind {
|
||||
DataDiffKind::None => HoverItemColor::Normal,
|
||||
DataDiffKind::Replace => HoverItemColor::Special,
|
||||
DataDiffKind::Delete => HoverItemColor::Delete,
|
||||
DataDiffKind::Insert => HoverItemColor::Insert,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn data_row_ui(
|
||||
ui: &mut egui::Ui,
|
||||
obj: Option<&Object>,
|
||||
base_address: usize,
|
||||
row_address: usize,
|
||||
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||
base_address: u64,
|
||||
row_address: u64,
|
||||
diff_row: &DataDiffRow,
|
||||
appearance: &Appearance,
|
||||
column: usize,
|
||||
) {
|
||||
if diffs.iter().any(|(dd, rds)| {
|
||||
dd.kind != DataDiffKind::None || rds.iter().any(|rd| rd.kind != DataDiffKind::None)
|
||||
}) {
|
||||
if diff_row.segments.iter().any(|dd| dd.kind != DataDiffKind::None)
|
||||
|| diff_row.relocations.iter().any(|rd| rd.kind != DataDiffKind::None)
|
||||
{
|
||||
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
||||
}
|
||||
let mut job = LayoutJob::default();
|
||||
@@ -137,20 +74,21 @@ pub(crate) fn data_row_ui(
|
||||
let mut cur_addr = 0usize;
|
||||
// The offset into the actual bytes of the section on this side, ignoring differences.
|
||||
let mut cur_addr_actual = base_address + row_address;
|
||||
for (diff, reloc_diffs) in diffs {
|
||||
for diff in diff_row.segments.iter() {
|
||||
let base_color = get_color_for_diff_kind(diff.kind, appearance);
|
||||
if diff.data.is_empty() {
|
||||
let mut str = " ".repeat(diff.len);
|
||||
let mut str = " ".repeat(diff.size);
|
||||
let n1 = cur_addr / 8;
|
||||
let n2 = (diff.len + cur_addr) / 8;
|
||||
let n2 = (diff.size + cur_addr) / 8;
|
||||
str.push_str(" ".repeat(n2 - n1).as_str());
|
||||
write_text(str.as_str(), base_color, &mut job, appearance.code_font.clone());
|
||||
cur_addr += diff.len;
|
||||
cur_addr += diff.size;
|
||||
} else {
|
||||
for byte in &diff.data {
|
||||
let mut byte_text = format!("{byte:02x} ");
|
||||
let mut byte_color = base_color;
|
||||
if let Some(reloc_diff) = reloc_diffs
|
||||
if let Some(reloc_diff) = diff_row
|
||||
.relocations
|
||||
.iter()
|
||||
.find(|reloc_diff| reloc_diff.range.contains(&cur_addr_actual))
|
||||
{
|
||||
@@ -179,11 +117,11 @@ pub(crate) fn data_row_ui(
|
||||
write_text(str.as_str(), appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
write_text(" ", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
for (diff, _) in diffs {
|
||||
for diff in diff_row.segments.iter() {
|
||||
let base_color = get_color_for_diff_kind(diff.kind, appearance);
|
||||
if diff.data.is_empty() {
|
||||
write_text(
|
||||
" ".repeat(diff.len).as_str(),
|
||||
" ".repeat(diff.size).as_str(),
|
||||
base_color,
|
||||
&mut job,
|
||||
appearance.code_font.clone(),
|
||||
@@ -204,70 +142,7 @@ pub(crate) fn data_row_ui(
|
||||
|
||||
let response = Label::new(job).sense(Sense::click()).ui(ui);
|
||||
if let Some(obj) = obj {
|
||||
response.context_menu(|ui| data_row_context_menu(ui, obj, diffs, column, appearance));
|
||||
response.on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diffs, appearance));
|
||||
response.context_menu(|ui| data_row_context_menu(ui, obj, diff_row, column, appearance));
|
||||
response.on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diff_row, appearance));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn split_diffs(
|
||||
diffs: &[DataDiff],
|
||||
reloc_diffs: &[DataRelocationDiff],
|
||||
symbol_offset_in_section: usize,
|
||||
) -> Vec<Vec<(DataDiff, Vec<DataRelocationDiff>)>> {
|
||||
let mut split_diffs = Vec::<Vec<(DataDiff, Vec<DataRelocationDiff>)>>::new();
|
||||
let mut row_diffs = Vec::<(DataDiff, Vec<DataRelocationDiff>)>::new();
|
||||
// The offset shown on the side of the GUI, shifted by insertions/deletions.
|
||||
let mut cur_addr = 0usize;
|
||||
// The offset into the actual bytes of the section on this side, ignoring differences.
|
||||
let mut cur_addr_actual = symbol_offset_in_section;
|
||||
for diff in diffs {
|
||||
let mut cur_len = 0usize;
|
||||
while cur_len < diff.len {
|
||||
let remaining_len = diff.len - cur_len;
|
||||
let mut remaining_in_row = BYTES_PER_ROW - (cur_addr % BYTES_PER_ROW);
|
||||
let len = min(remaining_len, remaining_in_row);
|
||||
|
||||
let data_diff = DataDiff {
|
||||
data: if diff.data.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
diff.data[cur_len..cur_len + len].to_vec()
|
||||
},
|
||||
kind: diff.kind,
|
||||
len,
|
||||
symbol: String::new(), // TODO
|
||||
};
|
||||
let row_reloc_diffs: Vec<DataRelocationDiff> = if diff.data.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let diff_range = cur_addr_actual + cur_len..cur_addr_actual + cur_len + len;
|
||||
reloc_diffs
|
||||
.iter()
|
||||
.filter_map(|reloc_diff| {
|
||||
if reloc_diff.range.start < diff_range.end
|
||||
&& diff_range.start < reloc_diff.range.end
|
||||
{
|
||||
Some(reloc_diff.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let row_diff = (data_diff, row_reloc_diffs);
|
||||
|
||||
row_diffs.push(row_diff);
|
||||
remaining_in_row -= len;
|
||||
cur_len += len;
|
||||
cur_addr += len;
|
||||
if remaining_in_row == 0 {
|
||||
split_diffs.push(take(&mut row_diffs));
|
||||
}
|
||||
}
|
||||
cur_addr_actual += diff.data.len();
|
||||
}
|
||||
if !row_diffs.is_empty() {
|
||||
split_diffs.push(take(&mut row_diffs));
|
||||
}
|
||||
split_diffs
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use objdiff_core::{
|
||||
build::BuildStatus,
|
||||
diff::{
|
||||
DiffObjConfig, ObjectDiff, SymbolDiff,
|
||||
data::BYTES_PER_ROW,
|
||||
display::{ContextItem, HoverItem, HoverItemColor, SymbolFilter, SymbolNavigationKind},
|
||||
},
|
||||
obj::{Object, Symbol},
|
||||
@@ -14,7 +15,7 @@ use crate::{
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_strips, render_table},
|
||||
data_diff::{BYTES_PER_ROW, data_row_ui, split_diffs},
|
||||
data_diff::data_row_ui,
|
||||
extab_diff::extab_ui,
|
||||
function_diff::{FunctionDiffContext, asm_col_ui},
|
||||
symbol_diff::{
|
||||
@@ -470,28 +471,14 @@ pub fn diff_view_ui(
|
||||
{
|
||||
// Joint diff view
|
||||
hotkeys::check_scroll_hotkeys(ui, true);
|
||||
let left_total_bytes =
|
||||
left_symbol_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
|
||||
let right_total_bytes =
|
||||
right_symbol_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
|
||||
if left_total_bytes != right_total_bytes {
|
||||
ui.label("Data size mismatch");
|
||||
let total_rows = left_symbol_diff.data_rows.len();
|
||||
if total_rows != right_symbol_diff.data_rows.len() {
|
||||
ui.label("Row count mismatch");
|
||||
return;
|
||||
}
|
||||
if left_total_bytes == 0 {
|
||||
if total_rows == 0 {
|
||||
return;
|
||||
}
|
||||
let total_rows = (left_total_bytes - 1) / BYTES_PER_ROW + 1;
|
||||
let left_diffs = split_diffs(
|
||||
&left_symbol_diff.data_diff,
|
||||
&left_symbol_diff.data_reloc_diff,
|
||||
left_symbol.address as usize,
|
||||
);
|
||||
let right_diffs = split_diffs(
|
||||
&right_symbol_diff.data_diff,
|
||||
&right_symbol_diff.data_reloc_diff,
|
||||
right_symbol.address as usize,
|
||||
);
|
||||
render_table(
|
||||
ui,
|
||||
available_width,
|
||||
@@ -500,15 +487,15 @@ pub fn diff_view_ui(
|
||||
total_rows,
|
||||
|row, column| {
|
||||
let i = row.index();
|
||||
let row_offset = i * BYTES_PER_ROW;
|
||||
let row_offset = i as u64 * BYTES_PER_ROW as u64;
|
||||
row.col(|ui| {
|
||||
if column == 0 {
|
||||
data_row_ui(
|
||||
ui,
|
||||
Some(left_obj),
|
||||
left_symbol.address as usize,
|
||||
left_symbol.address,
|
||||
row_offset,
|
||||
&left_diffs[i],
|
||||
&left_symbol_diff.data_rows[i],
|
||||
appearance,
|
||||
column,
|
||||
);
|
||||
@@ -516,9 +503,9 @@ pub fn diff_view_ui(
|
||||
data_row_ui(
|
||||
ui,
|
||||
Some(right_obj),
|
||||
right_symbol.address as usize,
|
||||
right_symbol.address,
|
||||
row_offset,
|
||||
&right_diffs[i],
|
||||
&right_symbol_diff.data_rows[i],
|
||||
appearance,
|
||||
column,
|
||||
);
|
||||
@@ -618,17 +605,10 @@ fn diff_col_ui(
|
||||
extab_ui(ui, ctx, appearance, column);
|
||||
} else if state.current_view == View::DataDiff {
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
let total_bytes =
|
||||
symbol_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
|
||||
if total_bytes == 0 {
|
||||
let total_rows = symbol_diff.data_rows.len();
|
||||
if total_rows == 0 {
|
||||
return ret;
|
||||
}
|
||||
let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1;
|
||||
let diffs = split_diffs(
|
||||
&symbol_diff.data_diff,
|
||||
&symbol_diff.data_reloc_diff,
|
||||
symbol.address as usize,
|
||||
);
|
||||
render_table(
|
||||
ui,
|
||||
available_width / 2.0,
|
||||
@@ -637,14 +617,14 @@ fn diff_col_ui(
|
||||
total_rows,
|
||||
|row, _column| {
|
||||
let i = row.index();
|
||||
let row_offset = i * BYTES_PER_ROW;
|
||||
let row_offset = i as u64 * BYTES_PER_ROW as u64;
|
||||
row.col(|ui| {
|
||||
data_row_ui(
|
||||
ui,
|
||||
Some(obj),
|
||||
symbol.address as usize,
|
||||
symbol.address,
|
||||
row_offset,
|
||||
&diffs[i],
|
||||
&symbol_diff.data_rows[i],
|
||||
appearance,
|
||||
column,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user