##// END OF EJS Templates
manifest: increase lrucache from 3 to 4...
Durham Goode -
r20075:f8737bce default
parent child Browse files
Show More
@@ -1,217 +1,218 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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 i18n import _
8 from i18n import _
9 import mdiff, parsers, error, revlog, util, dicthelpers
9 import mdiff, parsers, error, revlog, util, dicthelpers
10 import array, struct
10 import array, struct
11
11
12 class manifestdict(dict):
12 class manifestdict(dict):
13 def __init__(self, mapping=None, flags=None):
13 def __init__(self, mapping=None, flags=None):
14 if mapping is None:
14 if mapping is None:
15 mapping = {}
15 mapping = {}
16 if flags is None:
16 if flags is None:
17 flags = {}
17 flags = {}
18 dict.__init__(self, mapping)
18 dict.__init__(self, mapping)
19 self._flags = flags
19 self._flags = flags
20 def flags(self, f):
20 def flags(self, f):
21 return self._flags.get(f, "")
21 return self._flags.get(f, "")
22 def withflags(self):
22 def withflags(self):
23 return set(self._flags.keys())
23 return set(self._flags.keys())
24 def set(self, f, flags):
24 def set(self, f, flags):
25 self._flags[f] = flags
25 self._flags[f] = flags
26 def copy(self):
26 def copy(self):
27 return manifestdict(self, dict.copy(self._flags))
27 return manifestdict(self, dict.copy(self._flags))
28 def flagsdiff(self, d2):
28 def flagsdiff(self, d2):
29 return dicthelpers.diff(self._flags, d2._flags, "")
29 return dicthelpers.diff(self._flags, d2._flags, "")
30
30
31 class manifest(revlog.revlog):
31 class manifest(revlog.revlog):
32 def __init__(self, opener):
32 def __init__(self, opener):
33 # we expect to deal with not more than three revs at a time in merge
33 # we expect to deal with not more than four revs at a time,
34 self._mancache = util.lrucachedict(3)
34 # during a commit --amend
35 self._mancache = util.lrucachedict(4)
35 revlog.revlog.__init__(self, opener, "00manifest.i")
36 revlog.revlog.__init__(self, opener, "00manifest.i")
36
37
37 def parse(self, lines):
38 def parse(self, lines):
38 mfdict = manifestdict()
39 mfdict = manifestdict()
39 parsers.parse_manifest(mfdict, mfdict._flags, lines)
40 parsers.parse_manifest(mfdict, mfdict._flags, lines)
40 return mfdict
41 return mfdict
41
42
42 def readdelta(self, node):
43 def readdelta(self, node):
43 r = self.rev(node)
44 r = self.rev(node)
44 return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
45 return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
45
46
46 def readfast(self, node):
47 def readfast(self, node):
47 '''use the faster of readdelta or read'''
48 '''use the faster of readdelta or read'''
48 r = self.rev(node)
49 r = self.rev(node)
49 deltaparent = self.deltaparent(r)
50 deltaparent = self.deltaparent(r)
50 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
51 if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r):
51 return self.readdelta(node)
52 return self.readdelta(node)
52 return self.read(node)
53 return self.read(node)
53
54
54 def read(self, node):
55 def read(self, node):
55 if node == revlog.nullid:
56 if node == revlog.nullid:
56 return manifestdict() # don't upset local cache
57 return manifestdict() # don't upset local cache
57 if node in self._mancache:
58 if node in self._mancache:
58 return self._mancache[node][0]
59 return self._mancache[node][0]
59 text = self.revision(node)
60 text = self.revision(node)
60 arraytext = array.array('c', text)
61 arraytext = array.array('c', text)
61 mapping = self.parse(text)
62 mapping = self.parse(text)
62 self._mancache[node] = (mapping, arraytext)
63 self._mancache[node] = (mapping, arraytext)
63 return mapping
64 return mapping
64
65
65 def _search(self, m, s, lo=0, hi=None):
66 def _search(self, m, s, lo=0, hi=None):
66 '''return a tuple (start, end) that says where to find s within m.
67 '''return a tuple (start, end) that says where to find s within m.
67
68
68 If the string is found m[start:end] are the line containing
69 If the string is found m[start:end] are the line containing
69 that string. If start == end the string was not found and
70 that string. If start == end the string was not found and
70 they indicate the proper sorted insertion point.
71 they indicate the proper sorted insertion point.
71
72
72 m should be a buffer or a string
73 m should be a buffer or a string
73 s is a string'''
74 s is a string'''
74 def advance(i, c):
75 def advance(i, c):
75 while i < lenm and m[i] != c:
76 while i < lenm and m[i] != c:
76 i += 1
77 i += 1
77 return i
78 return i
78 if not s:
79 if not s:
79 return (lo, lo)
80 return (lo, lo)
80 lenm = len(m)
81 lenm = len(m)
81 if not hi:
82 if not hi:
82 hi = lenm
83 hi = lenm
83 while lo < hi:
84 while lo < hi:
84 mid = (lo + hi) // 2
85 mid = (lo + hi) // 2
85 start = mid
86 start = mid
86 while start > 0 and m[start - 1] != '\n':
87 while start > 0 and m[start - 1] != '\n':
87 start -= 1
88 start -= 1
88 end = advance(start, '\0')
89 end = advance(start, '\0')
89 if m[start:end] < s:
90 if m[start:end] < s:
90 # we know that after the null there are 40 bytes of sha1
91 # we know that after the null there are 40 bytes of sha1
91 # this translates to the bisect lo = mid + 1
92 # this translates to the bisect lo = mid + 1
92 lo = advance(end + 40, '\n') + 1
93 lo = advance(end + 40, '\n') + 1
93 else:
94 else:
94 # this translates to the bisect hi = mid
95 # this translates to the bisect hi = mid
95 hi = start
96 hi = start
96 end = advance(lo, '\0')
97 end = advance(lo, '\0')
97 found = m[lo:end]
98 found = m[lo:end]
98 if s == found:
99 if s == found:
99 # we know that after the null there are 40 bytes of sha1
100 # we know that after the null there are 40 bytes of sha1
100 end = advance(end + 40, '\n')
101 end = advance(end + 40, '\n')
101 return (lo, end + 1)
102 return (lo, end + 1)
102 else:
103 else:
103 return (lo, lo)
104 return (lo, lo)
104
105
105 def find(self, node, f):
106 def find(self, node, f):
106 '''look up entry for a single file efficiently.
107 '''look up entry for a single file efficiently.
107 return (node, flags) pair if found, (None, None) if not.'''
108 return (node, flags) pair if found, (None, None) if not.'''
108 if node in self._mancache:
109 if node in self._mancache:
109 mapping = self._mancache[node][0]
110 mapping = self._mancache[node][0]
110 return mapping.get(f), mapping.flags(f)
111 return mapping.get(f), mapping.flags(f)
111 text = self.revision(node)
112 text = self.revision(node)
112 start, end = self._search(text, f)
113 start, end = self._search(text, f)
113 if start == end:
114 if start == end:
114 return None, None
115 return None, None
115 l = text[start:end]
116 l = text[start:end]
116 f, n = l.split('\0')
117 f, n = l.split('\0')
117 return revlog.bin(n[:40]), n[40:-1]
118 return revlog.bin(n[:40]), n[40:-1]
118
119
119 def add(self, map, transaction, link, p1=None, p2=None,
120 def add(self, map, transaction, link, p1=None, p2=None,
120 changed=None):
121 changed=None):
121 # apply the changes collected during the bisect loop to our addlist
122 # apply the changes collected during the bisect loop to our addlist
122 # return a delta suitable for addrevision
123 # return a delta suitable for addrevision
123 def addlistdelta(addlist, x):
124 def addlistdelta(addlist, x):
124 # for large addlist arrays, building a new array is cheaper
125 # for large addlist arrays, building a new array is cheaper
125 # than repeatedly modifying the existing one
126 # than repeatedly modifying the existing one
126 currentposition = 0
127 currentposition = 0
127 newaddlist = array.array('c')
128 newaddlist = array.array('c')
128
129
129 for start, end, content in x:
130 for start, end, content in x:
130 newaddlist += addlist[currentposition:start]
131 newaddlist += addlist[currentposition:start]
131 if content:
132 if content:
132 newaddlist += array.array('c', content)
133 newaddlist += array.array('c', content)
133
134
134 currentposition = end
135 currentposition = end
135
136
136 newaddlist += addlist[currentposition:]
137 newaddlist += addlist[currentposition:]
137
138
138 deltatext = "".join(struct.pack(">lll", start, end, len(content))
139 deltatext = "".join(struct.pack(">lll", start, end, len(content))
139 + content for start, end, content in x)
140 + content for start, end, content in x)
140 return deltatext, newaddlist
141 return deltatext, newaddlist
141
142
142 def checkforbidden(l):
143 def checkforbidden(l):
143 for f in l:
144 for f in l:
144 if '\n' in f or '\r' in f:
145 if '\n' in f or '\r' in f:
145 raise error.RevlogError(
146 raise error.RevlogError(
146 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
147 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
147
148
148 # if we're using the cache, make sure it is valid and
149 # if we're using the cache, make sure it is valid and
149 # parented by the same node we're diffing against
150 # parented by the same node we're diffing against
150 if not (changed and p1 and (p1 in self._mancache)):
151 if not (changed and p1 and (p1 in self._mancache)):
151 files = sorted(map)
152 files = sorted(map)
152 checkforbidden(files)
153 checkforbidden(files)
153
154
154 # if this is changed to support newlines in filenames,
155 # if this is changed to support newlines in filenames,
155 # be sure to check the templates/ dir again (especially *-raw.tmpl)
156 # be sure to check the templates/ dir again (especially *-raw.tmpl)
156 hex, flags = revlog.hex, map.flags
157 hex, flags = revlog.hex, map.flags
157 text = ''.join("%s\0%s%s\n" % (f, hex(map[f]), flags(f))
158 text = ''.join("%s\0%s%s\n" % (f, hex(map[f]), flags(f))
158 for f in files)
159 for f in files)
159 arraytext = array.array('c', text)
160 arraytext = array.array('c', text)
160 cachedelta = None
161 cachedelta = None
161 else:
162 else:
162 added, removed = changed
163 added, removed = changed
163 addlist = self._mancache[p1][1]
164 addlist = self._mancache[p1][1]
164
165
165 checkforbidden(added)
166 checkforbidden(added)
166 # combine the changed lists into one list for sorting
167 # combine the changed lists into one list for sorting
167 work = [(x, False) for x in added]
168 work = [(x, False) for x in added]
168 work.extend((x, True) for x in removed)
169 work.extend((x, True) for x in removed)
169 # this could use heapq.merge() (from Python 2.6+) or equivalent
170 # this could use heapq.merge() (from Python 2.6+) or equivalent
170 # since the lists are already sorted
171 # since the lists are already sorted
171 work.sort()
172 work.sort()
172
173
173 delta = []
174 delta = []
174 dstart = None
175 dstart = None
175 dend = None
176 dend = None
176 dline = [""]
177 dline = [""]
177 start = 0
178 start = 0
178 # zero copy representation of addlist as a buffer
179 # zero copy representation of addlist as a buffer
179 addbuf = util.buffer(addlist)
180 addbuf = util.buffer(addlist)
180
181
181 # start with a readonly loop that finds the offset of
182 # start with a readonly loop that finds the offset of
182 # each line and creates the deltas
183 # each line and creates the deltas
183 for f, todelete in work:
184 for f, todelete in work:
184 # bs will either be the index of the item or the insert point
185 # bs will either be the index of the item or the insert point
185 start, end = self._search(addbuf, f, start)
186 start, end = self._search(addbuf, f, start)
186 if not todelete:
187 if not todelete:
187 l = "%s\0%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
188 l = "%s\0%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
188 else:
189 else:
189 if start == end:
190 if start == end:
190 # item we want to delete was not found, error out
191 # item we want to delete was not found, error out
191 raise AssertionError(
192 raise AssertionError(
192 _("failed to remove %s from manifest") % f)
193 _("failed to remove %s from manifest") % f)
193 l = ""
194 l = ""
194 if dstart is not None and dstart <= start and dend >= start:
195 if dstart is not None and dstart <= start and dend >= start:
195 if dend < end:
196 if dend < end:
196 dend = end
197 dend = end
197 if l:
198 if l:
198 dline.append(l)
199 dline.append(l)
199 else:
200 else:
200 if dstart is not None:
201 if dstart is not None:
201 delta.append([dstart, dend, "".join(dline)])
202 delta.append([dstart, dend, "".join(dline)])
202 dstart = start
203 dstart = start
203 dend = end
204 dend = end
204 dline = [l]
205 dline = [l]
205
206
206 if dstart is not None:
207 if dstart is not None:
207 delta.append([dstart, dend, "".join(dline)])
208 delta.append([dstart, dend, "".join(dline)])
208 # apply the delta to the addlist, and get a delta for addrevision
209 # apply the delta to the addlist, and get a delta for addrevision
209 deltatext, addlist = addlistdelta(addlist, delta)
210 deltatext, addlist = addlistdelta(addlist, delta)
210 cachedelta = (self.rev(p1), deltatext)
211 cachedelta = (self.rev(p1), deltatext)
211 arraytext = addlist
212 arraytext = addlist
212 text = util.buffer(arraytext)
213 text = util.buffer(arraytext)
213
214
214 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
215 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
215 self._mancache[n] = (map, arraytext)
216 self._mancache[n] = (map, arraytext)
216
217
217 return n
218 return n
General Comments 0
You need to be logged in to leave comments. Login now