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