##// END OF EJS Templates
mpatch: move collect() to module level...
Augie Fackler -
r28589:c4c7be9f default
parent child Browse files
Show More
@@ -1,120 +1,120 b''
1 1 # mpatch.py - Python implementation of mpatch.c
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import cStringIO
11 11 import struct
12 12
13 13 StringIO = cStringIO.StringIO
14 14
15 15 # This attempts to apply a series of patches in time proportional to
16 16 # the total size of the patches, rather than patches * len(text). This
17 17 # means rather than shuffling strings around, we shuffle around
18 18 # pointers to fragments with fragment lists.
19 19 #
20 20 # When the fragment lists get too long, we collapse them. To do this
21 21 # efficiently, we do all our operations inside a buffer created by
22 22 # mmap and simply use memmove. This avoids creating a bunch of large
23 23 # temporary string buffers.
24 24
25 25 def _pull(dst, src, l): # pull l bytes from src
26 26 while l:
27 27 f = src.pop()
28 28 if f[0] > l: # do we need to split?
29 29 src.append((f[0] - l, f[1] + l))
30 30 dst.append((l, f[1]))
31 31 return
32 32 dst.append(f)
33 33 l -= f[0]
34 34
35 35 def _move(m, dest, src, count):
36 36 """move count bytes from src to dest
37 37
38 38 The file pointer is left at the end of dest.
39 39 """
40 40 m.seek(src)
41 41 buf = m.read(count)
42 42 m.seek(dest)
43 43 m.write(buf)
44 44
45 def _collect(m, buf, list):
46 start = buf
47 for l, p in reversed(list):
48 _move(m, buf, p, l)
49 buf += l
50 return (buf - start, start)
51
45 52 def patches(a, bins):
46 53 if not bins:
47 54 return a
48 55
49 56 plens = [len(x) for x in bins]
50 57 pl = sum(plens)
51 58 bl = len(a) + pl
52 59 tl = bl + bl + pl # enough for the patches and two working texts
53 60 b1, b2 = 0, bl
54 61
55 62 if not tl:
56 63 return a
57 64
58 65 m = StringIO()
59 66
60 67 # load our original text
61 68 m.write(a)
62 69 frags = [(len(a), b1)]
63 70
64 71 # copy all the patches into our segment so we can memmove from them
65 72 pos = b2 + bl
66 73 m.seek(pos)
67 74 for p in bins: m.write(p)
68 75
69 def collect(buf, list):
70 start = buf
71 for l, p in reversed(list):
72 _move(m, buf, p, l)
73 buf += l
74 return (buf - start, start)
75
76 76 for plen in plens:
77 77 # if our list gets too long, execute it
78 78 if len(frags) > 128:
79 79 b2, b1 = b1, b2
80 frags = [collect(b1, frags)]
80 frags = [_collect(m, b1, frags)]
81 81
82 82 new = []
83 83 end = pos + plen
84 84 last = 0
85 85 while pos < end:
86 86 m.seek(pos)
87 87 p1, p2, l = struct.unpack(">lll", m.read(12))
88 88 _pull(new, frags, p1 - last) # what didn't change
89 89 _pull([], frags, p2 - p1) # what got deleted
90 90 new.append((l, pos + 12)) # what got added
91 91 pos += l + 12
92 92 last = p2
93 93 frags.extend(reversed(new)) # what was left at the end
94 94
95 t = collect(b2, frags)
95 t = _collect(m, b2, frags)
96 96
97 97 m.seek(t[1])
98 98 return m.read(t[0])
99 99
100 100 def patchedsize(orig, delta):
101 101 outlen, last, bin = 0, 0, 0
102 102 binend = len(delta)
103 103 data = 12
104 104
105 105 while data <= binend:
106 106 decode = delta[bin:bin + 12]
107 107 start, end, length = struct.unpack(">lll", decode)
108 108 if start > end:
109 109 break
110 110 bin = data + length
111 111 data = bin + 12
112 112 outlen += start - last
113 113 last = end
114 114 outlen += length
115 115
116 116 if bin != binend:
117 117 raise ValueError("patch cannot be decoded")
118 118
119 119 outlen += orig - last
120 120 return outlen
General Comments 0
You need to be logged in to leave comments. Login now