mercurial/linelog.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43472 acc4047c2194
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    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")