blob: 1f580c946133ed9057e3e9dff28608cba4350ccf [file] [log] [blame]
Amin Hassanib05a65a2017-12-18 15:15:32 -08001#!/usr/bin/python2
Mike Frysinger3ba7a082017-10-06 01:23:28 -04002#
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Block diff utility."""
8
Amin Hassanib05a65a2017-12-18 15:15:32 -08009from __future__ import print_function
10
Amin Hassani52b60392017-12-19 10:53:24 -080011# pylint: disable=import-error
12import argparse
Mike Frysinger3ba7a082017-10-06 01:23:28 -040013import sys
14
15
16class BlockDiffError(Exception):
17 pass
18
19
20def BlockDiff(block_size, file1, file2, name1, name2, max_length=-1):
21 """Performs a binary diff of two files by blocks.
22
23 Args:
24 block_size: the size of a block to diff by
25 file1: first file object
26 file2: second file object
27 name1: name of first file (for error reporting)
28 name2: name of second file (for error reporting)
29 max_length: the maximum length to read/diff in bytes (optional)
30 Returns:
31 A list of (start, length) pairs representing block extents that differ
32 between the two files.
33 Raises:
34 BlockDiffError if there were errors while diffing.
35
36 """
37 if max_length < 0:
38 max_length = sys.maxint
39 diff_list = []
40 num_blocks = extent_start = extent_length = 0
41 while max_length or extent_length:
42 read_length = min(max_length, block_size)
43 data1 = file1.read(read_length)
44 data2 = file2.read(read_length)
45 if len(data1) != len(data2):
46 raise BlockDiffError('read %d bytes from %s but %d bytes from %s' %
47 (len(data1), name1, len(data2), name2))
48
49 if data1 != data2:
50 # Data is different, mark it down.
51 if extent_length:
52 # Stretch the current diff extent.
53 extent_length += 1
54 else:
55 # Start a new diff extent.
56 extent_start = num_blocks
57 extent_length = 1
58 elif extent_length:
59 # Record the previous extent.
60 diff_list.append((extent_start, extent_length))
61 extent_length = 0
62
63 # Are we done reading?
64 if not data1:
65 break
66
67 max_length -= len(data1)
68 num_blocks += 1
69
70 return diff_list
71
72
73def main(argv):
74 # Parse command-line arguments.
Amin Hassani52b60392017-12-19 10:53:24 -080075 parser = argparse.ArgumentParser(
76 description='Compare FILE1 and FILE2 by blocks.',
77 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
Mike Frysinger3ba7a082017-10-06 01:23:28 -040078
Amin Hassani52b60392017-12-19 10:53:24 -080079 parser.add_argument('-b', '--block-size', metavar='NUM', type=int,
80 default=4096, help='the block size to use')
81 parser.add_argument('-m', '--max-length', metavar='NUM', type=int, default=-1,
82 help='maximum number of bytes to compare')
83 parser.add_argument('file1', metavar='FILE1')
84 parser.add_argument('file2', metavar='FILE2')
Mike Frysinger3ba7a082017-10-06 01:23:28 -040085
Amin Hassani52b60392017-12-19 10:53:24 -080086 args = parser.parse_args(argv[1:])
Mike Frysinger3ba7a082017-10-06 01:23:28 -040087
88 # Perform the block diff.
89 try:
Amin Hassani52b60392017-12-19 10:53:24 -080090 with open(args.file1) as file1:
91 with open(args.file2) as file2:
92 diff_list = BlockDiff(args.block_size, file1, file2,
93 args.file1, args.file2, args.max_length)
Mike Frysinger3ba7a082017-10-06 01:23:28 -040094 except BlockDiffError as e:
Amin Hassanib05a65a2017-12-18 15:15:32 -080095 print('Error: ' % e, file=sys.stderr)
Mike Frysinger3ba7a082017-10-06 01:23:28 -040096 return 2
97
98 # Print the diff, if such was found.
99 if diff_list:
100 total_diff_blocks = 0
101 for extent_start, extent_length in diff_list:
102 total_diff_blocks += extent_length
103 print('%d->%d (%d)' %
104 (extent_start, extent_start + extent_length, extent_length))
105
Amin Hassanib05a65a2017-12-18 15:15:32 -0800106 print('total diff: %d blocks' % total_diff_blocks)
Mike Frysinger3ba7a082017-10-06 01:23:28 -0400107 return 1
108
109 return 0
110
111
112if __name__ == '__main__':
113 sys.exit(main(sys.argv))