24 import struct |
24 import struct |
25 |
25 |
26 from .thirdparty import attr |
26 from .thirdparty import attr |
27 from . import pycompat |
27 from . import pycompat |
28 |
28 |
29 _llentry = struct.Struct('>II') |
29 _llentry = struct.Struct(b'>II') |
30 |
30 |
31 |
31 |
32 class LineLogError(Exception): |
32 class LineLogError(Exception): |
33 """Error raised when something bad happens internally in linelog.""" |
33 """Error raised when something bad happens internally in linelog.""" |
34 |
34 |
120 class _jump(_llinstruction): |
120 class _jump(_llinstruction): |
121 """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.""" |
122 |
122 |
123 def __init__(self, op1, op2): |
123 def __init__(self, op1, op2): |
124 if op1 != 0: |
124 if op1 != 0: |
125 raise LineLogError("malformed JUMP, op1 must be 0, got %d" % op1) |
125 raise LineLogError(b"malformed JUMP, op1 must be 0, got %d" % op1) |
126 self._target = op2 |
126 self._target = op2 |
127 |
127 |
128 def __str__(self): |
128 def __str__(self): |
129 return r'JUMP %d' % (self._target) |
129 return r'JUMP %d' % (self._target) |
130 |
130 |
141 class _eof(_llinstruction): |
141 class _eof(_llinstruction): |
142 """EOF is expressed as a JGE that always jumps to 0.""" |
142 """EOF is expressed as a JGE that always jumps to 0.""" |
143 |
143 |
144 def __init__(self, op1, op2): |
144 def __init__(self, op1, op2): |
145 if op1 != 0: |
145 if op1 != 0: |
146 raise LineLogError("malformed EOF, op1 must be 0, got %d" % op1) |
146 raise LineLogError(b"malformed EOF, op1 must be 0, got %d" % op1) |
147 if op2 != 0: |
147 if op2 != 0: |
148 raise LineLogError("malformed EOF, op2 must be 0, got %d" % op2) |
148 raise LineLogError(b"malformed EOF, op2 must be 0, got %d" % op2) |
149 |
149 |
150 def __str__(self): |
150 def __str__(self): |
151 return r'EOF' |
151 return r'EOF' |
152 |
152 |
153 def __eq__(self, other): |
153 def __eq__(self, other): |
216 def _decodeone(data, offset): |
216 def _decodeone(data, offset): |
217 """Decode a single linelog instruction from an offset in a buffer.""" |
217 """Decode a single linelog instruction from an offset in a buffer.""" |
218 try: |
218 try: |
219 op1, op2 = _llentry.unpack_from(data, offset) |
219 op1, op2 = _llentry.unpack_from(data, offset) |
220 except struct.error as e: |
220 except struct.error as e: |
221 raise LineLogError('reading an instruction failed: %r' % e) |
221 raise LineLogError(b'reading an instruction failed: %r' % e) |
222 opcode = op1 & 0b11 |
222 opcode = op1 & 0b11 |
223 op1 = op1 >> 2 |
223 op1 = op1 >> 2 |
224 if opcode == 0: |
224 if opcode == 0: |
225 if op1 == 0: |
225 if op1 == 0: |
226 if op2 == 0: |
226 if op2 == 0: |
229 return _jge(op1, op2) |
229 return _jge(op1, op2) |
230 elif opcode == 1: |
230 elif opcode == 1: |
231 return _jl(op1, op2) |
231 return _jl(op1, op2) |
232 elif opcode == 2: |
232 elif opcode == 2: |
233 return _line(op1, op2) |
233 return _line(op1, op2) |
234 raise NotImplementedError('Unimplemented opcode %r' % opcode) |
234 raise NotImplementedError(b'Unimplemented opcode %r' % opcode) |
235 |
235 |
236 |
236 |
237 class linelog(object): |
237 class linelog(object): |
238 """Efficient cache for per-line history information.""" |
238 """Efficient cache for per-line history information.""" |
239 |
239 |
253 and self._program == other._program |
253 and self._program == other._program |
254 and self._maxrev == other._maxrev |
254 and self._maxrev == other._maxrev |
255 ) |
255 ) |
256 |
256 |
257 def __repr__(self): |
257 def __repr__(self): |
258 return '<linelog at %s: maxrev=%d size=%d>' % ( |
258 return b'<linelog at %s: maxrev=%d size=%d>' % ( |
259 hex(id(self)), |
259 hex(id(self)), |
260 self._maxrev, |
260 self._maxrev, |
261 len(self._program), |
261 len(self._program), |
262 ) |
262 ) |
263 |
263 |
264 def debugstr(self): |
264 def debugstr(self): |
265 fmt = r'%%%dd %%s' % len(str(len(self._program))) |
265 fmt = r'%%%dd %%s' % len(str(len(self._program))) |
266 return pycompat.sysstr('\n').join( |
266 return pycompat.sysstr(b'\n').join( |
267 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 ) |
268 ) |
269 |
269 |
270 @classmethod |
270 @classmethod |
271 def fromdata(cls, buf): |
271 def fromdata(cls, buf): |
272 if len(buf) % _llentry.size != 0: |
272 if len(buf) % _llentry.size != 0: |
273 raise LineLogError( |
273 raise LineLogError( |
274 "invalid linelog buffer size %d (must be a multiple of %d)" |
274 b"invalid linelog buffer size %d (must be a multiple of %d)" |
275 % (len(buf), _llentry.size) |
275 % (len(buf), _llentry.size) |
276 ) |
276 ) |
277 expected = len(buf) / _llentry.size |
277 expected = len(buf) / _llentry.size |
278 fakejge = _decodeone(buf, 0) |
278 fakejge = _decodeone(buf, 0) |
279 if isinstance(fakejge, _jump): |
279 if isinstance(fakejge, _jump): |
281 else: |
281 else: |
282 maxrev = fakejge._cmprev |
282 maxrev = fakejge._cmprev |
283 numentries = fakejge._target |
283 numentries = fakejge._target |
284 if expected != numentries: |
284 if expected != numentries: |
285 raise LineLogError( |
285 raise LineLogError( |
286 "corrupt linelog data: claimed" |
286 b"corrupt linelog data: claimed" |
287 " %d entries but given data for %d entries" |
287 b" %d entries but given data for %d entries" |
288 % (expected, numentries) |
288 % (expected, numentries) |
289 ) |
289 ) |
290 instructions = [_eof(0, 0)] |
290 instructions = [_eof(0, 0)] |
291 for offset in pycompat.xrange(1, numentries): |
291 for offset in pycompat.xrange(1, numentries): |
292 instructions.append(_decodeone(buf, offset * _llentry.size)) |
292 instructions.append(_decodeone(buf, offset * _llentry.size)) |
293 return cls(instructions, maxrev=maxrev) |
293 return cls(instructions, maxrev=maxrev) |
294 |
294 |
295 def encode(self): |
295 def encode(self): |
296 hdr = _jge(self._maxrev, len(self._program)).encode() |
296 hdr = _jge(self._maxrev, len(self._program)).encode() |
297 return hdr + ''.join(i.encode() for i in self._program[1:]) |
297 return hdr + b''.join(i.encode() for i in self._program[1:]) |
298 |
298 |
299 def clear(self): |
299 def clear(self): |
300 self._program = [] |
300 self._program = [] |
301 self._maxrev = 0 |
301 self._maxrev = 0 |
302 self._lastannotate = None |
302 self._lastannotate = None |
318 else: |
318 else: |
319 ar = self.annotate(rev) |
319 ar = self.annotate(rev) |
320 # ar = self.annotate(self._maxrev) |
320 # ar = self.annotate(self._maxrev) |
321 if a1 > len(ar.lines): |
321 if a1 > len(ar.lines): |
322 raise LineLogError( |
322 raise LineLogError( |
323 '%d contains %d lines, tried to access line %d' |
323 b'%d contains %d lines, tried to access line %d' |
324 % (rev, len(ar.lines), a1) |
324 % (rev, len(ar.lines), a1) |
325 ) |
325 ) |
326 elif a1 == len(ar.lines): |
326 elif a1 == len(ar.lines): |
327 # Simulated EOF instruction since we're at EOF, which |
327 # Simulated EOF instruction since we're at EOF, which |
328 # doesn't have a "real" line. |
328 # doesn't have a "real" line. |
354 appendinst(_line(newrev, newlinenum)) |
354 appendinst(_line(newrev, newlinenum)) |
355 # delete |
355 # delete |
356 if a1 < a2: |
356 if a1 < a2: |
357 if a2 > len(ar.lines): |
357 if a2 > len(ar.lines): |
358 raise LineLogError( |
358 raise LineLogError( |
359 '%d contains %d lines, tried to access line %d' |
359 b'%d contains %d lines, tried to access line %d' |
360 % (rev, len(ar.lines), a2) |
360 % (rev, len(ar.lines), a2) |
361 ) |
361 ) |
362 elif a2 == len(ar.lines): |
362 elif a2 == len(ar.lines): |
363 endaddr = ar._eof |
363 endaddr = ar._eof |
364 else: |
364 else: |
452 elif isinstance(inst, (_jl, _jge)): |
452 elif isinstance(inst, (_jl, _jge)): |
453 pass |
453 pass |
454 elif isinstance(inst, _line): |
454 elif isinstance(inst, _line): |
455 lines.append((inst._rev, inst._origlineno)) |
455 lines.append((inst._rev, inst._origlineno)) |
456 else: |
456 else: |
457 raise LineLogError("Illegal instruction %r" % inst) |
457 raise LineLogError(b"Illegal instruction %r" % inst) |
458 if nextpc == end: |
458 if nextpc == end: |
459 return lines |
459 return lines |
460 pc = nextpc |
460 pc = nextpc |
461 raise LineLogError("Failed to perform getalllines") |
461 raise LineLogError(b"Failed to perform getalllines") |