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