blob: 5793def7e88333050c985eed0dd6899f6378a488 [file] [log] [blame]
Amin Hassanib05a65a2017-12-18 15:15:32 -08001#!/usr/bin/python2
Mike Frysinger3ba7a082017-10-06 01:23:28 -04002#
Amin Hassanif94b6432018-01-26 17:39:47 -08003# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Mike Frysinger3ba7a082017-10-06 01:23:28 -040017
18"""Block diff utility."""
19
Amin Hassanib05a65a2017-12-18 15:15:32 -080020from __future__ import print_function
21
Amin Hassani52b60392017-12-19 10:53:24 -080022# pylint: disable=import-error
23import argparse
Mike Frysinger3ba7a082017-10-06 01:23:28 -040024import sys
25
26
27class BlockDiffError(Exception):
28 pass
29
30
31def BlockDiff(block_size, file1, file2, name1, name2, max_length=-1):
32 """Performs a binary diff of two files by blocks.
33
34 Args:
35 block_size: the size of a block to diff by
36 file1: first file object
37 file2: second file object
38 name1: name of first file (for error reporting)
39 name2: name of second file (for error reporting)
40 max_length: the maximum length to read/diff in bytes (optional)
41 Returns:
42 A list of (start, length) pairs representing block extents that differ
43 between the two files.
44 Raises:
45 BlockDiffError if there were errors while diffing.
46
47 """
48 if max_length < 0:
49 max_length = sys.maxint
50 diff_list = []
51 num_blocks = extent_start = extent_length = 0
52 while max_length or extent_length:
53 read_length = min(max_length, block_size)
54 data1 = file1.read(read_length)
55 data2 = file2.read(read_length)
56 if len(data1) != len(data2):
57 raise BlockDiffError('read %d bytes from %s but %d bytes from %s' %
58 (len(data1), name1, len(data2), name2))
59
60 if data1 != data2:
61 # Data is different, mark it down.
62 if extent_length:
63 # Stretch the current diff extent.
64 extent_length += 1
65 else:
66 # Start a new diff extent.
67 extent_start = num_blocks
68 extent_length = 1
69 elif extent_length:
70 # Record the previous extent.
71 diff_list.append((extent_start, extent_length))
72 extent_length = 0
73
74 # Are we done reading?
75 if not data1:
76 break
77
78 max_length -= len(data1)
79 num_blocks += 1
80
81 return diff_list
82
83
84def main(argv):
85 # Parse command-line arguments.
Amin Hassani52b60392017-12-19 10:53:24 -080086 parser = argparse.ArgumentParser(
87 description='Compare FILE1 and FILE2 by blocks.',
88 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
Mike Frysinger3ba7a082017-10-06 01:23:28 -040089
Amin Hassani52b60392017-12-19 10:53:24 -080090 parser.add_argument('-b', '--block-size', metavar='NUM', type=int,
91 default=4096, help='the block size to use')
92 parser.add_argument('-m', '--max-length', metavar='NUM', type=int, default=-1,
93 help='maximum number of bytes to compare')
94 parser.add_argument('file1', metavar='FILE1')
95 parser.add_argument('file2', metavar='FILE2')
Mike Frysinger3ba7a082017-10-06 01:23:28 -040096
Amin Hassani52b60392017-12-19 10:53:24 -080097 args = parser.parse_args(argv[1:])
Mike Frysinger3ba7a082017-10-06 01:23:28 -040098
99 # Perform the block diff.
100 try:
Amin Hassani52b60392017-12-19 10:53:24 -0800101 with open(args.file1) as file1:
102 with open(args.file2) as file2:
103 diff_list = BlockDiff(args.block_size, file1, file2,
104 args.file1, args.file2, args.max_length)
Mike Frysinger3ba7a082017-10-06 01:23:28 -0400105 except BlockDiffError as e:
Amin Hassanib05a65a2017-12-18 15:15:32 -0800106 print('Error: ' % e, file=sys.stderr)
Mike Frysinger3ba7a082017-10-06 01:23:28 -0400107 return 2
108
109 # Print the diff, if such was found.
110 if diff_list:
111 total_diff_blocks = 0
112 for extent_start, extent_length in diff_list:
113 total_diff_blocks += extent_length
114 print('%d->%d (%d)' %
115 (extent_start, extent_start + extent_length, extent_length))
116
Amin Hassanib05a65a2017-12-18 15:15:32 -0800117 print('total diff: %d blocks' % total_diff_blocks)
Mike Frysinger3ba7a082017-10-06 01:23:28 -0400118 return 1
119
120 return 0
121
122
123if __name__ == '__main__':
124 sys.exit(main(sys.argv))