Mercurial > hg
comparison mercurial/manifest.py @ 47043:12450fbea288
manifests: push down expected node length into the parser
This strictly enforces the node length in the manifest lines according
to what the repository expects. One test case moves large hash testing
into the non-treemanifest part as treemanifests don't provide an
interface for overriding just the node length for now.
Differential Revision: https://phab.mercurial-scm.org/D10533
author | Joerg Sonnenberger <joerg@bec.de> |
---|---|
date | Fri, 30 Apr 2021 02:11:58 +0200 |
parents | d55b71393907 |
children | 4c041c71ec01 |
comparison
equal
deleted
inserted
replaced
47042:c5e1cc0b4c77 | 47043:12450fbea288 |
---|---|
40 | 40 |
41 # Allow tests to more easily test the alternate path in manifestdict.fastdelta() | 41 # Allow tests to more easily test the alternate path in manifestdict.fastdelta() |
42 FASTDELTA_TEXTDIFF_THRESHOLD = 1000 | 42 FASTDELTA_TEXTDIFF_THRESHOLD = 1000 |
43 | 43 |
44 | 44 |
45 def _parse(data): | 45 def _parse(nodelen, data): |
46 # This method does a little bit of excessive-looking | 46 # This method does a little bit of excessive-looking |
47 # precondition checking. This is so that the behavior of this | 47 # precondition checking. This is so that the behavior of this |
48 # class exactly matches its C counterpart to try and help | 48 # class exactly matches its C counterpart to try and help |
49 # prevent surprise breakage for anyone that develops against | 49 # prevent surprise breakage for anyone that develops against |
50 # the pure version. | 50 # the pure version. |
61 if flags in _manifestflags: | 61 if flags in _manifestflags: |
62 n = n[:-1] | 62 n = n[:-1] |
63 nl -= 1 | 63 nl -= 1 |
64 else: | 64 else: |
65 flags = b'' | 65 flags = b'' |
66 if nl not in (40, 64): | 66 if nl != 2 * nodelen: |
67 raise ValueError(b'Invalid manifest line') | 67 raise ValueError(b'Invalid manifest line') |
68 | 68 |
69 yield f, bin(n), flags | 69 yield f, bin(n), flags |
70 | 70 |
71 | 71 |
129 if flags in _manifestflags: | 129 if flags in _manifestflags: |
130 hlen = nlpos - zeropos - 2 | 130 hlen = nlpos - zeropos - 2 |
131 else: | 131 else: |
132 hlen = nlpos - zeropos - 1 | 132 hlen = nlpos - zeropos - 1 |
133 flags = b'' | 133 flags = b'' |
134 if hlen not in (40, 64): | 134 if hlen != 2 * self.lm._nodelen: |
135 raise error.StorageError(b'Invalid manifest line') | 135 raise error.StorageError(b'Invalid manifest line') |
136 hashval = unhexlify( | 136 hashval = unhexlify( |
137 data, self.lm.extrainfo[self.pos], zeropos + 1, hlen | 137 data, self.lm.extrainfo[self.pos], zeropos + 1, hlen |
138 ) | 138 ) |
139 self.pos += 1 | 139 self.pos += 1 |
174 modified since the manifest was created or compacted. | 174 modified since the manifest was created or compacted. |
175 """ | 175 """ |
176 | 176 |
177 def __init__( | 177 def __init__( |
178 self, | 178 self, |
179 nodelen, | |
179 data, | 180 data, |
180 positions=None, | 181 positions=None, |
181 extrainfo=None, | 182 extrainfo=None, |
182 extradata=None, | 183 extradata=None, |
183 hasremovals=False, | 184 hasremovals=False, |
184 ): | 185 ): |
186 self._nodelen = nodelen | |
185 if positions is None: | 187 if positions is None: |
186 self.positions = self.findlines(data) | 188 self.positions = self.findlines(data) |
187 self.extrainfo = [0] * len(self.positions) | 189 self.extrainfo = [0] * len(self.positions) |
188 self.data = data | 190 self.data = data |
189 self.extradata = [] | 191 self.extradata = [] |
286 flags = data[nlpos - 1 : nlpos] | 288 flags = data[nlpos - 1 : nlpos] |
287 if flags in _manifestflags: | 289 if flags in _manifestflags: |
288 hlen -= 1 | 290 hlen -= 1 |
289 else: | 291 else: |
290 flags = b'' | 292 flags = b'' |
291 if hlen not in (40, 64): | 293 if hlen != 2 * self._nodelen: |
292 raise error.StorageError(b'Invalid manifest line') | 294 raise error.StorageError(b'Invalid manifest line') |
293 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, hlen) | 295 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, hlen) |
294 return (hashval, flags) | 296 return (hashval, flags) |
295 | 297 |
296 def __delitem__(self, key): | 298 def __delitem__(self, key): |
342 ) | 344 ) |
343 | 345 |
344 def copy(self): | 346 def copy(self): |
345 # XXX call _compact like in C? | 347 # XXX call _compact like in C? |
346 return _lazymanifest( | 348 return _lazymanifest( |
349 self._nodelen, | |
347 self.data, | 350 self.data, |
348 self.positions, | 351 self.positions, |
349 self.extrainfo, | 352 self.extrainfo, |
350 self.extradata, | 353 self.extradata, |
351 self.hasremovals, | 354 self.hasremovals, |
452 def __len__(self): | 455 def __len__(self): |
453 return len(self.positions) | 456 return len(self.positions) |
454 | 457 |
455 def filtercopy(self, filterfn): | 458 def filtercopy(self, filterfn): |
456 # XXX should be optimized | 459 # XXX should be optimized |
457 c = _lazymanifest(b'') | 460 c = _lazymanifest(self._nodelen, b'') |
458 for f, n, fl in self.iterentries(): | 461 for f, n, fl in self.iterentries(): |
459 if filterfn(f): | 462 if filterfn(f): |
460 c[f] = n, fl | 463 c[f] = n, fl |
461 return c | 464 return c |
462 | 465 |
467 pass | 470 pass |
468 | 471 |
469 | 472 |
470 @interfaceutil.implementer(repository.imanifestdict) | 473 @interfaceutil.implementer(repository.imanifestdict) |
471 class manifestdict(object): | 474 class manifestdict(object): |
472 def __init__(self, data=b''): | 475 def __init__(self, nodelen, data=b''): |
473 self._lm = _lazymanifest(data) | 476 self._nodelen = nodelen |
477 self._lm = _lazymanifest(nodelen, data) | |
474 | 478 |
475 def __getitem__(self, key): | 479 def __getitem__(self, key): |
476 return self._lm[key][0] | 480 return self._lm[key][0] |
477 | 481 |
478 def find(self, key): | 482 def find(self, key): |
576 '''generate a new manifest filtered by the match argument''' | 580 '''generate a new manifest filtered by the match argument''' |
577 if match.always(): | 581 if match.always(): |
578 return self.copy() | 582 return self.copy() |
579 | 583 |
580 if self._filesfastpath(match): | 584 if self._filesfastpath(match): |
581 m = manifestdict() | 585 m = manifestdict(self._nodelen) |
582 lm = self._lm | 586 lm = self._lm |
583 for fn in match.files(): | 587 for fn in match.files(): |
584 if fn in lm: | 588 if fn in lm: |
585 m._lm[fn] = lm[fn] | 589 m._lm[fn] = lm[fn] |
586 return m | 590 return m |
587 | 591 |
588 m = manifestdict() | 592 m = manifestdict(self._nodelen) |
589 m._lm = self._lm.filtercopy(match) | 593 m._lm = self._lm.filtercopy(match) |
590 return m | 594 return m |
591 | 595 |
592 def diff(self, m2, match=None, clean=False): | 596 def diff(self, m2, match=None, clean=False): |
593 """Finds changes between the current manifest and m2. | 597 """Finds changes between the current manifest and m2. |
626 return self._lm[key][1] | 630 return self._lm[key][1] |
627 except KeyError: | 631 except KeyError: |
628 return b'' | 632 return b'' |
629 | 633 |
630 def copy(self): | 634 def copy(self): |
631 c = manifestdict() | 635 c = manifestdict(self._nodelen) |
632 c._lm = self._lm.copy() | 636 c._lm = self._lm.copy() |
633 return c | 637 return c |
634 | 638 |
635 def items(self): | 639 def items(self): |
636 return (x[:2] for x in self._lm.iterentries()) | 640 return (x[:2] for x in self._lm.iterentries()) |
793 class treemanifest(object): | 797 class treemanifest(object): |
794 def __init__(self, nodeconstants, dir=b'', text=b''): | 798 def __init__(self, nodeconstants, dir=b'', text=b''): |
795 self._dir = dir | 799 self._dir = dir |
796 self.nodeconstants = nodeconstants | 800 self.nodeconstants = nodeconstants |
797 self._node = self.nodeconstants.nullid | 801 self._node = self.nodeconstants.nullid |
802 self._nodelen = self.nodeconstants.nodelen | |
798 self._loadfunc = _noop | 803 self._loadfunc = _noop |
799 self._copyfunc = _noop | 804 self._copyfunc = _noop |
800 self._dirty = False | 805 self._dirty = False |
801 self._dirs = {} | 806 self._dirs = {} |
802 self._lazydirs = {} | 807 self._lazydirs = {} |
1320 def unmodifiedsince(self, m2): | 1325 def unmodifiedsince(self, m2): |
1321 return not self._dirty and not m2._dirty and self._node == m2._node | 1326 return not self._dirty and not m2._dirty and self._node == m2._node |
1322 | 1327 |
1323 def parse(self, text, readsubtree): | 1328 def parse(self, text, readsubtree): |
1324 selflazy = self._lazydirs | 1329 selflazy = self._lazydirs |
1325 for f, n, fl in _parse(text): | 1330 for f, n, fl in _parse(self._nodelen, text): |
1326 if fl == b't': | 1331 if fl == b't': |
1327 f = f + b'/' | 1332 f = f + b'/' |
1328 # False below means "doesn't need to be copied" and can use the | 1333 # False below means "doesn't need to be copied" and can use the |
1329 # cached value from readsubtree directly. | 1334 # cached value from readsubtree directly. |
1330 selflazy[f] = (n, readsubtree, False) | 1335 selflazy[f] = (n, readsubtree, False) |
2017 | 2022 |
2018 @interfaceutil.implementer(repository.imanifestrevisionwritable) | 2023 @interfaceutil.implementer(repository.imanifestrevisionwritable) |
2019 class memmanifestctx(object): | 2024 class memmanifestctx(object): |
2020 def __init__(self, manifestlog): | 2025 def __init__(self, manifestlog): |
2021 self._manifestlog = manifestlog | 2026 self._manifestlog = manifestlog |
2022 self._manifestdict = manifestdict() | 2027 self._manifestdict = manifestdict(manifestlog.nodeconstants.nodelen) |
2023 | 2028 |
2024 def _storage(self): | 2029 def _storage(self): |
2025 return self._manifestlog.getstorage(b'') | 2030 return self._manifestlog.getstorage(b'') |
2026 | 2031 |
2027 def copy(self): | 2032 def copy(self): |
2079 def parents(self): | 2084 def parents(self): |
2080 return self._storage().parents(self._node) | 2085 return self._storage().parents(self._node) |
2081 | 2086 |
2082 def read(self): | 2087 def read(self): |
2083 if self._data is None: | 2088 if self._data is None: |
2084 if self._node == self._manifestlog.nodeconstants.nullid: | 2089 nc = self._manifestlog.nodeconstants |
2085 self._data = manifestdict() | 2090 if self._node == nc.nullid: |
2091 self._data = manifestdict(nc.nodelen) | |
2086 else: | 2092 else: |
2087 store = self._storage() | 2093 store = self._storage() |
2088 if self._node in store.fulltextcache: | 2094 if self._node in store.fulltextcache: |
2089 text = pycompat.bytestr(store.fulltextcache[self._node]) | 2095 text = pycompat.bytestr(store.fulltextcache[self._node]) |
2090 else: | 2096 else: |
2091 text = store.revision(self._node) | 2097 text = store.revision(self._node) |
2092 arraytext = bytearray(text) | 2098 arraytext = bytearray(text) |
2093 store.fulltextcache[self._node] = arraytext | 2099 store.fulltextcache[self._node] = arraytext |
2094 self._data = manifestdict(text) | 2100 self._data = manifestdict(nc.nodelen, text) |
2095 return self._data | 2101 return self._data |
2096 | 2102 |
2097 def readfast(self, shallow=False): | 2103 def readfast(self, shallow=False): |
2098 """Calls either readdelta or read, based on which would be less work. | 2104 """Calls either readdelta or read, based on which would be less work. |
2099 readdelta is called if the delta is against the p1, and therefore can be | 2105 readdelta is called if the delta is against the p1, and therefore can be |
2116 Changing the value of `shallow` has no effect on flat manifests. | 2122 Changing the value of `shallow` has no effect on flat manifests. |
2117 """ | 2123 """ |
2118 store = self._storage() | 2124 store = self._storage() |
2119 r = store.rev(self._node) | 2125 r = store.rev(self._node) |
2120 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r)) | 2126 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r)) |
2121 return manifestdict(d) | 2127 return manifestdict(store.nodeconstants.nodelen, d) |
2122 | 2128 |
2123 def find(self, key): | 2129 def find(self, key): |
2124 return self.read().find(key) | 2130 return self.read().find(key) |
2125 | 2131 |
2126 | 2132 |
2242 """ | 2248 """ |
2243 store = self._storage() | 2249 store = self._storage() |
2244 if shallow: | 2250 if shallow: |
2245 r = store.rev(self._node) | 2251 r = store.rev(self._node) |
2246 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r)) | 2252 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r)) |
2247 return manifestdict(d) | 2253 return manifestdict(store.nodeconstants.nodelen, d) |
2248 else: | 2254 else: |
2249 # Need to perform a slow delta | 2255 # Need to perform a slow delta |
2250 r0 = store.deltaparent(store.rev(self._node)) | 2256 r0 = store.deltaparent(store.rev(self._node)) |
2251 m0 = self._manifestlog.get(self._dir, store.node(r0)).read() | 2257 m0 = self._manifestlog.get(self._dir, store.node(r0)).read() |
2252 m1 = self.read() | 2258 m1 = self.read() |
2271 deltaparent = store.deltaparent(r) | 2277 deltaparent = store.deltaparent(r) |
2272 if deltaparent != nullrev and deltaparent in store.parentrevs(r): | 2278 if deltaparent != nullrev and deltaparent in store.parentrevs(r): |
2273 return self.readdelta(shallow=shallow) | 2279 return self.readdelta(shallow=shallow) |
2274 | 2280 |
2275 if shallow: | 2281 if shallow: |
2276 return manifestdict(store.revision(self._node)) | 2282 return manifestdict( |
2283 store.nodeconstants.nodelen, store.revision(self._node) | |
2284 ) | |
2277 else: | 2285 else: |
2278 return self.read() | 2286 return self.read() |
2279 | 2287 |
2280 def find(self, key): | 2288 def find(self, key): |
2281 return self.read().find(key) | 2289 return self.read().find(key) |