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)