comparison mercurial/linelog.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents ee97f7a677f3
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
21 from __future__ import absolute_import, print_function 21 from __future__ import absolute_import, print_function
22 22
23 import abc 23 import abc
24 import struct 24 import struct
25 25
26 from .thirdparty import ( 26 from .thirdparty import attr
27 attr, 27 from . import pycompat
28 )
29 from . import (
30 pycompat,
31 )
32 28
33 _llentry = struct.Struct('>II') 29 _llentry = struct.Struct('>II')
30
34 31
35 class LineLogError(Exception): 32 class LineLogError(Exception):
36 """Error raised when something bad happens internally in linelog.""" 33 """Error raised when something bad happens internally in linelog."""
34
37 35
38 @attr.s 36 @attr.s
39 class lineinfo(object): 37 class lineinfo(object):
40 # Introducing revision of this line. 38 # Introducing revision of this line.
41 rev = attr.ib() 39 rev = attr.ib()
42 # Line number for this line in its introducing revision. 40 # Line number for this line in its introducing revision.
43 linenum = attr.ib() 41 linenum = attr.ib()
44 # Private. Offset in the linelog program of this line. Used internally. 42 # Private. Offset in the linelog program of this line. Used internally.
45 _offset = attr.ib() 43 _offset = attr.ib()
46 44
45
47 @attr.s 46 @attr.s
48 class annotateresult(object): 47 class annotateresult(object):
49 rev = attr.ib() 48 rev = attr.ib()
50 lines = attr.ib() 49 lines = attr.ib()
51 _eof = attr.ib() 50 _eof = attr.ib()
52 51
53 def __iter__(self): 52 def __iter__(self):
54 return iter(self.lines) 53 return iter(self.lines)
55 54
55
56 class _llinstruction(object): 56 class _llinstruction(object):
57 57
58 __metaclass__ = abc.ABCMeta 58 __metaclass__ = abc.ABCMeta
59 59
60 @abc.abstractmethod 60 @abc.abstractmethod
88 Returns: 88 Returns:
89 The new value of pc. Returns None if exeuction should stop 89 The new value of pc. Returns None if exeuction should stop
90 (that is, we've found the end of the file.) 90 (that is, we've found the end of the file.)
91 """ 91 """
92 92
93
93 class _jge(_llinstruction): 94 class _jge(_llinstruction):
94 """If the current rev is greater than or equal to op1, jump to op2.""" 95 """If the current rev is greater than or equal to op1, jump to op2."""
95 96
96 def __init__(self, op1, op2): 97 def __init__(self, op1, op2):
97 self._cmprev = op1 98 self._cmprev = op1
99 100
100 def __str__(self): 101 def __str__(self):
101 return r'JGE %d %d' % (self._cmprev, self._target) 102 return r'JGE %d %d' % (self._cmprev, self._target)
102 103
103 def __eq__(self, other): 104 def __eq__(self, other):
104 return (type(self) == type(other) 105 return (
105 and self._cmprev == other._cmprev 106 type(self) == type(other)
106 and self._target == other._target) 107 and self._cmprev == other._cmprev
108 and self._target == other._target
109 )
107 110
108 def encode(self): 111 def encode(self):
109 return _llentry.pack(self._cmprev << 2, self._target) 112 return _llentry.pack(self._cmprev << 2, self._target)
110 113
111 def execute(self, rev, pc, emit): 114 def execute(self, rev, pc, emit):
112 if rev >= self._cmprev: 115 if rev >= self._cmprev:
113 return self._target 116 return self._target
114 return pc + 1 117 return pc + 1
115 118
119
116 class _jump(_llinstruction): 120 class _jump(_llinstruction):
117 """Unconditional jumps are expressed as a JGE with op1 set to 0.""" 121 """Unconditional jumps are expressed as a JGE with op1 set to 0."""
118 122
119 def __init__(self, op1, op2): 123 def __init__(self, op1, op2):
120 if op1 != 0: 124 if op1 != 0:
123 127
124 def __str__(self): 128 def __str__(self):
125 return r'JUMP %d' % (self._target) 129 return r'JUMP %d' % (self._target)
126 130
127 def __eq__(self, other): 131 def __eq__(self, other):
128 return (type(self) == type(other) 132 return type(self) == type(other) and self._target == other._target
129 and self._target == other._target)
130 133
131 def encode(self): 134 def encode(self):
132 return _llentry.pack(0, self._target) 135 return _llentry.pack(0, self._target)
133 136
134 def execute(self, rev, pc, emit): 137 def execute(self, rev, pc, emit):
135 return self._target 138 return self._target
139
136 140
137 class _eof(_llinstruction): 141 class _eof(_llinstruction):
138 """EOF is expressed as a JGE that always jumps to 0.""" 142 """EOF is expressed as a JGE that always jumps to 0."""
139 143
140 def __init__(self, op1, op2): 144 def __init__(self, op1, op2):
153 return _llentry.pack(0, 0) 157 return _llentry.pack(0, 0)
154 158
155 def execute(self, rev, pc, emit): 159 def execute(self, rev, pc, emit):
156 return None 160 return None
157 161
162
158 class _jl(_llinstruction): 163 class _jl(_llinstruction):
159 """If the current rev is less than op1, jump to op2.""" 164 """If the current rev is less than op1, jump to op2."""
160 165
161 def __init__(self, op1, op2): 166 def __init__(self, op1, op2):
162 self._cmprev = op1 167 self._cmprev = op1
164 169
165 def __str__(self): 170 def __str__(self):
166 return r'JL %d %d' % (self._cmprev, self._target) 171 return r'JL %d %d' % (self._cmprev, self._target)
167 172
168 def __eq__(self, other): 173 def __eq__(self, other):
169 return (type(self) == type(other) 174 return (
170 and self._cmprev == other._cmprev 175 type(self) == type(other)
171 and self._target == other._target) 176 and self._cmprev == other._cmprev
177 and self._target == other._target
178 )
172 179
173 def encode(self): 180 def encode(self):
174 return _llentry.pack(1 | (self._cmprev << 2), self._target) 181 return _llentry.pack(1 | (self._cmprev << 2), self._target)
175 182
176 def execute(self, rev, pc, emit): 183 def execute(self, rev, pc, emit):
177 if rev < self._cmprev: 184 if rev < self._cmprev:
178 return self._target 185 return self._target
179 return pc + 1 186 return pc + 1
187
180 188
181 class _line(_llinstruction): 189 class _line(_llinstruction):
182 """Emit a line.""" 190 """Emit a line."""
183 191
184 def __init__(self, op1, op2): 192 def __init__(self, op1, op2):
189 197
190 def __str__(self): 198 def __str__(self):
191 return r'LINE %d %d' % (self._rev, self._origlineno) 199 return r'LINE %d %d' % (self._rev, self._origlineno)
192 200
193 def __eq__(self, other): 201 def __eq__(self, other):
194 return (type(self) == type(other) 202 return (
195 and self._rev == other._rev 203 type(self) == type(other)
196 and self._origlineno == other._origlineno) 204 and self._rev == other._rev
205 and self._origlineno == other._origlineno
206 )
197 207
198 def encode(self): 208 def encode(self):
199 return _llentry.pack(2 | (self._rev << 2), self._origlineno) 209 return _llentry.pack(2 | (self._rev << 2), self._origlineno)
200 210
201 def execute(self, rev, pc, emit): 211 def execute(self, rev, pc, emit):
202 emit(lineinfo(self._rev, self._origlineno, pc)) 212 emit(lineinfo(self._rev, self._origlineno, pc))
203 return pc + 1 213 return pc + 1
214
204 215
205 def _decodeone(data, offset): 216 def _decodeone(data, offset):
206 """Decode a single linelog instruction from an offset in a buffer.""" 217 """Decode a single linelog instruction from an offset in a buffer."""
207 try: 218 try:
208 op1, op2 = _llentry.unpack_from(data, offset) 219 op1, op2 = _llentry.unpack_from(data, offset)
220 return _jl(op1, op2) 231 return _jl(op1, op2)
221 elif opcode == 2: 232 elif opcode == 2:
222 return _line(op1, op2) 233 return _line(op1, op2)
223 raise NotImplementedError('Unimplemented opcode %r' % opcode) 234 raise NotImplementedError('Unimplemented opcode %r' % opcode)
224 235
236
225 class linelog(object): 237 class linelog(object):
226 """Efficient cache for per-line history information.""" 238 """Efficient cache for per-line history information."""
227 239
228 def __init__(self, program=None, maxrev=0): 240 def __init__(self, program=None, maxrev=0):
229 if program is None: 241 if program is None:
234 self._program = program 246 self._program = program
235 self._lastannotate = None 247 self._lastannotate = None
236 self._maxrev = maxrev 248 self._maxrev = maxrev
237 249
238 def __eq__(self, other): 250 def __eq__(self, other):
239 return (type(self) == type(other) 251 return (
240 and self._program == other._program 252 type(self) == type(other)
241 and self._maxrev == other._maxrev) 253 and self._program == other._program
254 and self._maxrev == other._maxrev
255 )
242 256
243 def __repr__(self): 257 def __repr__(self):
244 return '<linelog at %s: maxrev=%d size=%d>' % ( 258 return '<linelog at %s: maxrev=%d size=%d>' % (
245 hex(id(self)), self._maxrev, len(self._program)) 259 hex(id(self)),
260 self._maxrev,
261 len(self._program),
262 )
246 263
247 def debugstr(self): 264 def debugstr(self):
248 fmt = r'%%%dd %%s' % len(str(len(self._program))) 265 fmt = r'%%%dd %%s' % len(str(len(self._program)))
249 return pycompat.sysstr('\n').join( 266 return pycompat.sysstr('\n').join(
250 fmt % (idx, i) for idx, i in enumerate(self._program[1:], 1)) 267 fmt % (idx, i) for idx, i in enumerate(self._program[1:], 1)
268 )
251 269
252 @classmethod 270 @classmethod
253 def fromdata(cls, buf): 271 def fromdata(cls, buf):
254 if len(buf) % _llentry.size != 0: 272 if len(buf) % _llentry.size != 0:
255 raise LineLogError( 273 raise LineLogError(
256 "invalid linelog buffer size %d (must be a multiple of %d)" % ( 274 "invalid linelog buffer size %d (must be a multiple of %d)"
257 len(buf), _llentry.size)) 275 % (len(buf), _llentry.size)
276 )
258 expected = len(buf) / _llentry.size 277 expected = len(buf) / _llentry.size
259 fakejge = _decodeone(buf, 0) 278 fakejge = _decodeone(buf, 0)
260 if isinstance(fakejge, _jump): 279 if isinstance(fakejge, _jump):
261 maxrev = 0 280 maxrev = 0
262 else: 281 else:
263 maxrev = fakejge._cmprev 282 maxrev = fakejge._cmprev
264 numentries = fakejge._target 283 numentries = fakejge._target
265 if expected != numentries: 284 if expected != numentries:
266 raise LineLogError("corrupt linelog data: claimed" 285 raise LineLogError(
267 " %d entries but given data for %d entries" % ( 286 "corrupt linelog data: claimed"
268 expected, numentries)) 287 " %d entries but given data for %d entries"
288 % (expected, numentries)
289 )
269 instructions = [_eof(0, 0)] 290 instructions = [_eof(0, 0)]
270 for offset in pycompat.xrange(1, numentries): 291 for offset in pycompat.xrange(1, numentries):
271 instructions.append(_decodeone(buf, offset * _llentry.size)) 292 instructions.append(_decodeone(buf, offset * _llentry.size))
272 return cls(instructions, maxrev=maxrev) 293 return cls(instructions, maxrev=maxrev)
273 294
279 self._program = [] 300 self._program = []
280 self._maxrev = 0 301 self._maxrev = 0
281 self._lastannotate = None 302 self._lastannotate = None
282 303
283 def replacelines_vec(self, rev, a1, a2, blines): 304 def replacelines_vec(self, rev, a1, a2, blines):
284 return self.replacelines(rev, a1, a2, 0, len(blines), 305 return self.replacelines(
285 _internal_blines=blines) 306 rev, a1, a2, 0, len(blines), _internal_blines=blines
307 )
286 308
287 def replacelines(self, rev, a1, a2, b1, b2, _internal_blines=None): 309 def replacelines(self, rev, a1, a2, b1, b2, _internal_blines=None):
288 """Replace lines [a1, a2) with lines [b1, b2).""" 310 """Replace lines [a1, a2) with lines [b1, b2)."""
289 if self._lastannotate: 311 if self._lastannotate:
290 # TODO(augie): make replacelines() accept a revision at 312 # TODO(augie): make replacelines() accept a revision at
296 else: 318 else:
297 ar = self.annotate(rev) 319 ar = self.annotate(rev)
298 # ar = self.annotate(self._maxrev) 320 # ar = self.annotate(self._maxrev)
299 if a1 > len(ar.lines): 321 if a1 > len(ar.lines):
300 raise LineLogError( 322 raise LineLogError(
301 '%d contains %d lines, tried to access line %d' % ( 323 '%d contains %d lines, tried to access line %d'
302 rev, len(ar.lines), a1)) 324 % (rev, len(ar.lines), a1)
325 )
303 elif a1 == len(ar.lines): 326 elif a1 == len(ar.lines):
304 # Simulated EOF instruction since we're at EOF, which 327 # Simulated EOF instruction since we're at EOF, which
305 # doesn't have a "real" line. 328 # doesn't have a "real" line.
306 a1inst = _eof(0, 0) 329 a1inst = _eof(0, 0)
307 a1info = lineinfo(0, 0, ar._eof) 330 a1info = lineinfo(0, 0, ar._eof)
331 appendinst(_line(newrev, newlinenum)) 354 appendinst(_line(newrev, newlinenum))
332 # delete 355 # delete
333 if a1 < a2: 356 if a1 < a2:
334 if a2 > len(ar.lines): 357 if a2 > len(ar.lines):
335 raise LineLogError( 358 raise LineLogError(
336 '%d contains %d lines, tried to access line %d' % ( 359 '%d contains %d lines, tried to access line %d'
337 rev, len(ar.lines), a2)) 360 % (rev, len(ar.lines), a2)
361 )
338 elif a2 == len(ar.lines): 362 elif a2 == len(ar.lines):
339 endaddr = ar._eof 363 endaddr = ar._eof
340 else: 364 else:
341 endaddr = ar.lines[a2]._offset 365 endaddr = ar.lines[a2]._offset
342 if a2 > 0 and rev < self._maxrev: 366 if a2 > 0 and rev < self._maxrev:
382 lastpc = pc 406 lastpc = pc
383 pc = inst.execute(rev, pc, lines.append) 407 pc = inst.execute(rev, pc, lines.append)
384 executed += 1 408 executed += 1
385 if pc is not None: 409 if pc is not None:
386 raise LineLogError( 410 raise LineLogError(
387 r'Probably hit an infinite loop in linelog. Program:\n' + 411 r'Probably hit an infinite loop in linelog. Program:\n'
388 self.debugstr()) 412 + self.debugstr()
413 )
389 ar = annotateresult(rev, lines, lastpc) 414 ar = annotateresult(rev, lines, lastpc)
390 self._lastannotate = ar 415 self._lastannotate = ar
391 return ar 416 return ar
392 417
393 @property 418 @property