Skip to content

Commit fbdaa89

Browse files
authored
Refactor data diffing & expose WASM API (#256)
* Refactor data diffing & expose WASM API * Update test snapshots
1 parent f7cb494 commit fbdaa89

File tree

14 files changed

+444
-255
lines changed

14 files changed

+444
-255
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ default-members = [
1414
resolver = "3"
1515

1616
[workspace.package]
17-
version = "3.1.1"
17+
version = "3.2.0"
1818
authors = ["Luke Street <luke@street.dev>"]
1919
edition = "2024"
2020
license = "MIT OR Apache-2.0"

objdiff-core/src/diff/data.rs

Lines changed: 102 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use anyhow::{Result, anyhow};
55
use similar::{Algorithm, capture_diff_slices, get_diff_ratio};
66

77
use super::{
8-
DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
8+
DataDiff, DataDiffKind, DataDiffRow, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
99
code::{address_eq, section_name_eq},
1010
};
1111
use crate::obj::{Object, Relocation, ResolvedRelocation, Symbol, SymbolFlag, SymbolKind};
@@ -111,14 +111,12 @@ fn diff_data_range(left_data: &[u8], right_data: &[u8]) -> (f32, Vec<DataDiff>,
111111
left_data_diff.push(DataDiff {
112112
data: left_data[..len.min(left_data.len())].to_vec(),
113113
kind,
114-
len,
115-
..Default::default()
114+
size: len,
116115
});
117116
right_data_diff.push(DataDiff {
118117
data: right_data[..len.min(right_data.len())].to_vec(),
119118
kind,
120-
len,
121-
..Default::default()
119+
size: len,
122120
});
123121
if kind == DataDiffKind::Replace {
124122
match left_len.cmp(&right_len) {
@@ -127,29 +125,25 @@ fn diff_data_range(left_data: &[u8], right_data: &[u8]) -> (f32, Vec<DataDiff>,
127125
left_data_diff.push(DataDiff {
128126
data: vec![],
129127
kind: DataDiffKind::Insert,
130-
len,
131-
..Default::default()
128+
size: len,
132129
});
133130
right_data_diff.push(DataDiff {
134131
data: right_data[left_len..right_len].to_vec(),
135132
kind: DataDiffKind::Insert,
136-
len,
137-
..Default::default()
133+
size: len,
138134
});
139135
}
140136
Ordering::Greater => {
141137
let len = left_len - right_len;
142138
left_data_diff.push(DataDiff {
143139
data: left_data[right_len..left_len].to_vec(),
144140
kind: DataDiffKind::Delete,
145-
len,
146-
..Default::default()
141+
size: len,
147142
});
148143
right_data_diff.push(DataDiff {
149144
data: vec![],
150145
kind: DataDiffKind::Delete,
151-
len,
152-
..Default::default()
146+
size: len,
153147
});
154148
}
155149
Ordering::Equal => {}
@@ -219,16 +213,17 @@ fn diff_data_relocs_for_range<'left, 'right>(
219213

220214
pub fn no_diff_data_section(obj: &Object, section_idx: usize) -> Result<SectionDiff> {
221215
let section = &obj.sections[section_idx];
222-
let len = section.data.len();
223-
let data = &section.data[0..len];
224216

225-
let data_diff =
226-
vec![DataDiff { data: data.to_vec(), kind: DataDiffKind::None, len, ..Default::default() }];
217+
let data_diff = vec![DataDiff {
218+
data: section.data.0.clone(),
219+
kind: DataDiffKind::None,
220+
size: section.data.len(),
221+
}];
227222

228223
let mut reloc_diffs = Vec::new();
229224
for reloc in section.relocations.iter() {
230225
let reloc_len = obj.arch.data_reloc_size(reloc.flags);
231-
let range = reloc.address as usize..reloc.address as usize + reloc_len;
226+
let range = reloc.address..reloc.address + reloc_len as u64;
232227
reloc_diffs.push(DataRelocationDiff {
233228
reloc: reloc.clone(),
234229
kind: DataDiffKind::None,
@@ -279,8 +274,7 @@ pub fn diff_data_section(
279274
) {
280275
if let Some(left_reloc) = left_reloc {
281276
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
282-
let range = left_reloc.relocation.address as usize
283-
..left_reloc.relocation.address as usize + len;
277+
let range = left_reloc.relocation.address..left_reloc.relocation.address + len as u64;
284278
left_reloc_diffs.push(DataRelocationDiff {
285279
reloc: left_reloc.relocation.clone(),
286280
kind: diff_kind,
@@ -289,8 +283,7 @@ pub fn diff_data_section(
289283
}
290284
if let Some(right_reloc) = right_reloc {
291285
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
292-
let range = right_reloc.relocation.address as usize
293-
..right_reloc.relocation.address as usize + len;
286+
let range = right_reloc.relocation.address..right_reloc.relocation.address + len as u64;
294287
right_reloc_diffs.push(DataRelocationDiff {
295288
reloc: right_reloc.relocation.clone(),
296289
kind: diff_kind,
@@ -345,30 +338,32 @@ pub fn no_diff_data_symbol(obj: &Object, symbol_index: usize) -> Result<SymbolDi
345338
let range = start as usize..end as usize;
346339
let data = &section.data[range.clone()];
347340

348-
let len = symbol.size as usize;
349-
let data_diff =
350-
vec![DataDiff { data: data.to_vec(), kind: DataDiffKind::None, len, ..Default::default() }];
341+
let data_diff = vec![DataDiff {
342+
data: data.to_vec(),
343+
kind: DataDiffKind::None,
344+
size: symbol.size as usize,
345+
}];
351346

352347
let mut reloc_diffs = Vec::new();
353348
for reloc in section.relocations.iter() {
354349
if !range.contains(&(reloc.address as usize)) {
355350
continue;
356351
}
357352
let reloc_len = obj.arch.data_reloc_size(reloc.flags);
358-
let range = reloc.address as usize..reloc.address as usize + reloc_len;
353+
let range = reloc.address..reloc.address + reloc_len as u64;
359354
reloc_diffs.push(DataRelocationDiff {
360355
reloc: reloc.clone(),
361356
kind: DataDiffKind::None,
362357
range,
363358
});
364359
}
365360

361+
let data_rows = build_data_diff_rows(&data_diff, &reloc_diffs, symbol.address);
366362
Ok(SymbolDiff {
367363
target_symbol: None,
368364
match_percent: None,
369365
diff_score: None,
370-
data_diff,
371-
data_reloc_diff: reloc_diffs,
366+
data_rows,
372367
..Default::default()
373368
})
374369
}
@@ -454,8 +449,8 @@ pub fn diff_data_symbol(
454449

455450
if let Some(left_reloc) = left_reloc {
456451
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
457-
let range = left_reloc.relocation.address as usize
458-
..left_reloc.relocation.address as usize + len;
452+
let range =
453+
left_reloc.relocation.address..left_reloc.relocation.address + len as u64;
459454
left_reloc_diffs.push(DataRelocationDiff {
460455
reloc: left_reloc.relocation.clone(),
461456
kind: diff_kind,
@@ -464,8 +459,8 @@ pub fn diff_data_symbol(
464459
}
465460
if let Some(right_reloc) = right_reloc {
466461
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
467-
let range = right_reloc.relocation.address as usize
468-
..right_reloc.relocation.address as usize + len;
462+
let range =
463+
right_reloc.relocation.address..right_reloc.relocation.address + len as u64;
469464
right_reloc_diffs.push(DataRelocationDiff {
470465
reloc: right_reloc.relocation.clone(),
471466
kind: diff_kind,
@@ -486,23 +481,29 @@ pub fn diff_data_symbol(
486481
}
487482
}
488483

484+
left_reloc_diffs
485+
.sort_by(|a, b| a.range.start.cmp(&b.range.start).then(a.range.end.cmp(&b.range.end)));
486+
right_reloc_diffs
487+
.sort_by(|a, b| a.range.start.cmp(&b.range.start).then(a.range.end.cmp(&b.range.end)));
488+
489489
let match_percent = match_ratio * 100.0;
490+
let left_rows = build_data_diff_rows(&left_data_diff, &left_reloc_diffs, left_symbol.address);
491+
let right_rows =
492+
build_data_diff_rows(&right_data_diff, &right_reloc_diffs, right_symbol.address);
490493

491494
Ok((
492495
SymbolDiff {
493496
target_symbol: Some(right_symbol_idx),
494497
match_percent: Some(match_percent),
495498
diff_score: None,
496-
data_diff: left_data_diff,
497-
data_reloc_diff: left_reloc_diffs,
499+
data_rows: left_rows,
498500
..Default::default()
499501
},
500502
SymbolDiff {
501503
target_symbol: Some(left_symbol_idx),
502504
match_percent: Some(match_percent),
503505
diff_score: None,
504-
data_diff: right_data_diff,
505-
data_reloc_diff: right_reloc_diffs,
506+
data_rows: right_rows,
506507
..Default::default()
507508
},
508509
))
@@ -593,3 +594,68 @@ fn symbols_matching_section(
593594
&& !s.flags.contains(SymbolFlag::Ignored)
594595
})
595596
}
597+
598+
pub const BYTES_PER_ROW: usize = 16;
599+
600+
fn build_data_diff_row(
601+
data_diffs: &[DataDiff],
602+
reloc_diffs: &[DataRelocationDiff],
603+
symbol_address: u64,
604+
row_index: usize,
605+
) -> DataDiffRow {
606+
let row_start = row_index * BYTES_PER_ROW;
607+
let row_end = row_start + BYTES_PER_ROW;
608+
let mut row_diff = DataDiffRow {
609+
address: symbol_address + row_start as u64,
610+
segments: Vec::new(),
611+
relocations: Vec::new(),
612+
};
613+
614+
// Collect all segments that overlap with this row
615+
let mut current_offset = 0;
616+
for diff in data_diffs {
617+
let diff_end = current_offset + diff.size;
618+
if current_offset < row_end && diff_end > row_start {
619+
let start_in_diff = row_start.saturating_sub(current_offset);
620+
let end_in_diff = row_end.min(diff_end) - current_offset;
621+
if start_in_diff < end_in_diff {
622+
let data_slice = if diff.data.is_empty() {
623+
Vec::new()
624+
} else {
625+
diff.data[start_in_diff..end_in_diff.min(diff.data.len())].to_vec()
626+
};
627+
row_diff.segments.push(DataDiff {
628+
data: data_slice,
629+
kind: diff.kind,
630+
size: end_in_diff - start_in_diff,
631+
});
632+
}
633+
}
634+
current_offset = diff_end;
635+
if current_offset >= row_start + BYTES_PER_ROW {
636+
break;
637+
}
638+
}
639+
640+
// Collect all relocations that overlap with this row
641+
let row_end_absolute = row_diff.address + BYTES_PER_ROW as u64;
642+
row_diff.relocations = reloc_diffs
643+
.iter()
644+
.filter(|rd| rd.range.start < row_end_absolute && rd.range.end > row_diff.address)
645+
.cloned()
646+
.collect();
647+
648+
row_diff
649+
}
650+
651+
fn build_data_diff_rows(
652+
segments: &[DataDiff],
653+
relocations: &[DataRelocationDiff],
654+
symbol_address: u64,
655+
) -> Vec<DataDiffRow> {
656+
let total_len = segments.iter().map(|s| s.size as u64).sum::<u64>();
657+
let num_rows = total_len.div_ceil(BYTES_PER_ROW as u64) as usize;
658+
(0..num_rows)
659+
.map(|row_index| build_data_diff_row(segments, relocations, symbol_address, row_index))
660+
.collect()
661+
}

0 commit comments

Comments
 (0)