Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame^] | 1 | # Module 'cmpcache' |
| 2 | # |
| 3 | # Efficiently compare files, boolean outcome only (equal / not equal). |
| 4 | # |
| 5 | # Tricks (used in this order): |
| 6 | # - Use the statcache module to avoid statting files more than once |
| 7 | # - Files with identical type, size & mtime are assumed to be clones |
| 8 | # - Files with different type or size cannot be identical |
| 9 | # - We keep a cache of outcomes of earlier comparisons |
| 10 | # - We don't fork a process to run 'cmp' but read the files ourselves |
| 11 | # |
| 12 | # XXX There is a dependency on constants in <sys/stat.h> here. |
| 13 | |
| 14 | import posix |
| 15 | import statcache |
| 16 | |
| 17 | |
| 18 | # The cache. |
| 19 | # |
| 20 | cache = {} |
| 21 | |
| 22 | |
| 23 | # Compare two files, use the cache if possible. |
| 24 | # May raise posix.error if a stat or open of either fails. |
| 25 | # |
| 26 | def cmp(f1, f2): |
| 27 | # Return 1 for identical files, 0 for different. |
| 28 | # Raise exceptions if either file could not be statted, read, etc. |
| 29 | s1, s2 = sig(statcache.stat(f1)), sig(statcache.stat(f2)) |
| 30 | if s1[0] <> 8 or s2[0] <> 8: # XXX 8 is S_IFREG in <sys/stat.h> |
| 31 | # Either is a not a plain file -- always report as different |
| 32 | return 0 |
| 33 | if s1 = s2: |
| 34 | # type, size & mtime match -- report same |
| 35 | return 1 |
| 36 | if s1[:2] <> s2[:2]: # Types or sizes differ, don't bother |
| 37 | # types or sizes differ -- report different |
| 38 | return 0 |
| 39 | # same type and size -- look in the cache |
| 40 | key = f1 + ' ' + f2 |
| 41 | if cache.has_key(key): |
| 42 | cs1, cs2, outcome = cache[key] |
| 43 | # cache hit |
| 44 | if s1 = cs1 and s2 = cs2: |
| 45 | # cached signatures match |
| 46 | return outcome |
| 47 | # stale cached signature(s) |
| 48 | # really compare |
| 49 | outcome = do_cmp(f1, f2) |
| 50 | cache[key] = s1, s2, outcome |
| 51 | return outcome |
| 52 | |
| 53 | # Return signature (i.e., type, size, mtime) from raw stat data. |
| 54 | # |
| 55 | def sig(st): |
| 56 | # 0-5: st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid |
| 57 | # 6-9: st_size, st_atime, st_mtime, st_ctime |
| 58 | type = st[0] / 4096 # XXX dependent on S_IFMT in <sys/stat.h> |
| 59 | size = st[6] |
| 60 | mtime = st[8] |
| 61 | return type, size, mtime |
| 62 | |
| 63 | # Compare two files, really. |
| 64 | # |
| 65 | def do_cmp(f1, f2): |
| 66 | #print ' cmp', f1, f2 # XXX remove when debugged |
| 67 | bufsize = 8096 # Could be tuned |
| 68 | fp1 = open(f1, 'r') |
| 69 | fp2 = open(f2, 'r') |
| 70 | while 1: |
| 71 | b1 = fp1.read(bufsize) |
| 72 | b2 = fp2.read(bufsize) |
| 73 | if b1 <> b2: return 0 |
| 74 | if not b1: return 1 |