-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathbaseline.py
More file actions
46 lines (40 loc) · 1.65 KB
/
Copy pathbaseline.py
File metadata and controls
46 lines (40 loc) · 1.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""Round 2 baseline: corruption scanner.
Compares two equally-sized binary files and reports every contiguous run of
differing bytes as ``(offset, length)``.
"""
from __future__ import annotations
def find_corruptions(ref_path: str, cor_path: str) -> list[tuple[int, int]]:
"""Return ``[(offset, length), ...]`` for every differing byte range."""
# Step 1: read both files fully into memory as bytes objects.
with open(ref_path, "rb") as f:
ref = f.read()
with open(cor_path, "rb") as f:
cor = f.read()
if len(ref) != len(cor):
raise ValueError("reference and corrupted files differ in length")
# Step 2: walk both buffers in lockstep and record every position where
# the two files disagree. The result is a sorted list of standalone byte
# offsets, e.g. [3, 4, 5, 17, 18].
diffs: list[int] = []
for i in range(len(ref)):
if ref[i] != cor[i]:
diffs.append(i)
# Step 3: collapse runs of consecutive offsets into (start, length) ranges.
# The list from step 2 becomes [(3, 3), (17, 2)]: starting at 3 there are
# three differing bytes, then starting at 17 there are two more.
if not diffs:
return []
ranges: list[tuple[int, int]] = []
start = diffs[0]
prev = diffs[0]
for pos in diffs[1:]:
if pos == prev + 1:
# Still inside the current run; extend it.
prev = pos
else:
# Gap. Close the current run and start a new one.
ranges.append((start, prev - start + 1))
start = pos
prev = pos
ranges.append((start, prev - start + 1)) # Close the final run.
return ranges