1 # mpatch.py - Python implementation of mpatch.c
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
10 from cStringIO import StringIO
12 from StringIO import StringIO
14 # This attempts to apply a series of patches in time proportional to
15 # the total size of the patches, rather than patches * len(text). This
16 # means rather than shuffling strings around, we shuffle around
17 # pointers to fragments with fragment lists.
19 # When the fragment lists get too long, we collapse them. To do this
20 # efficiently, we do all our operations inside a buffer created by
21 # mmap and simply use memmove. This avoids creating a bunch of large
22 # temporary string buffers.
27 plens = [len(x) for x in bins]
30 tl = bl + bl + pl # enough for the patches and two working texts
36 def move(dest, src, count):
37 """move count bytes from src to dest
39 The file pointer is left at the end of dest.
46 # load our original text
48 frags = [(len(a), b1)]
50 # copy all the patches into our segment so we can memmove from them
53 for p in bins: m.write(p)
55 def pull(dst, src, l): # pull l bytes from src
58 if f[0] > l: # do we need to split?
59 src.insert(0, (f[0] - l, f[1] + l))
65 def collect(buf, list):
70 return (buf - start, start)
73 # if our list gets too long, execute it
76 frags = [collect(b1, frags)]
83 p1, p2, l = struct.unpack(">lll", m.read(12))
84 pull(new, frags, p1 - last) # what didn't change
85 pull([], frags, p2 - p1) # what got deleted
86 new.append((l, pos + 12)) # what got added
89 frags = new + frags # what was left at the end
91 t = collect(b2, frags)
96 def patchedsize(orig, delta):
97 outlen, last, bin = 0, 0, 0
101 while data <= binend:
102 decode = delta[bin:bin + 12]
103 start, end, length = struct.unpack(">lll", decode)
108 outlen += start - last
113 raise Exception("patch cannot be decoded")
115 outlen += orig - last