124 |
124 |
125 def _cmp(a, b): |
125 def _cmp(a, b): |
126 return (a > b) - (a < b) |
126 return (a > b) - (a < b) |
127 |
127 |
128 class _lazymanifest(object): |
128 class _lazymanifest(object): |
|
129 """A pure python manifest backed by a byte string. It is supplimented with |
|
130 internal lists as it is modified, until it is compacted back to a pure byte |
|
131 string. |
|
132 |
|
133 ``data`` is the initial manifest data. |
|
134 |
|
135 ``positions`` is a list of offsets, one per manifest entry. Positive |
|
136 values are offsets into ``data``, negative values are offsets into the |
|
137 ``extradata`` list. When an entry is removed, its entry is dropped from |
|
138 ``positions``. The values are encoded such that when walking the list and |
|
139 indexing into ``data`` or ``extradata`` as appropriate, the entries are |
|
140 sorted by filename. |
|
141 |
|
142 ``extradata`` is a list of (key, hash, flags) for entries that were added or |
|
143 modified since the manifest was created or compacted. |
|
144 """ |
129 def __init__(self, data, positions=None, extrainfo=None, extradata=None, |
145 def __init__(self, data, positions=None, extrainfo=None, extradata=None, |
130 hasremovals=False): |
146 hasremovals=False): |
131 if positions is None: |
147 if positions is None: |
132 self.positions = self.findlines(data) |
148 self.positions = self.findlines(data) |
133 self.extrainfo = [0] * len(self.positions) |
149 self.extrainfo = [0] * len(self.positions) |
244 raise KeyError |
260 raise KeyError |
245 cur = self.positions[needle] |
261 cur = self.positions[needle] |
246 self.positions = self.positions[:needle] + self.positions[needle + 1:] |
262 self.positions = self.positions[:needle] + self.positions[needle + 1:] |
247 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:] |
263 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:] |
248 if cur >= 0: |
264 if cur >= 0: |
|
265 # This does NOT unsort the list as far as the search functions are |
|
266 # concerned, as they only examine lines mapped by self.positions. |
249 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:] |
267 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:] |
250 self.hasremovals = True |
268 self.hasremovals = True |
251 |
269 |
252 def __setitem__(self, key, value): |
270 def __setitem__(self, key, value): |
253 if not isinstance(key, bytes): |
271 if not isinstance(key, bytes): |
295 self.extrainfo = [0] * len(self.positions) |
313 self.extrainfo = [0] * len(self.positions) |
296 while i < len(self.positions): |
314 while i < len(self.positions): |
297 if self.positions[i] >= 0: |
315 if self.positions[i] >= 0: |
298 cur = self.positions[i] |
316 cur = self.positions[i] |
299 last_cut = cur |
317 last_cut = cur |
|
318 |
|
319 # Collect all contiguous entries in the buffer at the current |
|
320 # offset, breaking out only for added/modified items held in |
|
321 # extradata, or a deleted line prior to the next position. |
300 while True: |
322 while True: |
301 self.positions[i] = offset |
323 self.positions[i] = offset |
302 i += 1 |
324 i += 1 |
303 if i == len(self.positions) or self.positions[i] < 0: |
325 if i == len(self.positions) or self.positions[i] < 0: |
304 break |
326 break |