parsers.py
213 lines
| 6.1 KiB
| text/x-python
|
PythonLexer
|
r7700 | # parsers.py - Python implementation of parsers.c | ||
# | ||||
# Copyright 2009 Matt Mackall <mpm@selenic.com> and others | ||||
# | ||||
|
r8225 | # This software may be used and distributed according to the terms of the | ||
|
r10263 | # GNU General Public License version 2 or any later version. | ||
|
r7700 | |||
|
r27339 | from __future__ import absolute_import | ||
import struct | ||||
import zlib | ||||
r43925 | from ..node import nullid, nullrev | |||
from .. import ( | ||||
pycompat, | ||||
revlogutils, | ||||
util, | ||||
) | ||||
|
r43346 | |||
|
r36976 | stringio = pycompat.bytesio | ||
|
r7700 | |||
|
r31220 | |||
|
r7700 | _pack = struct.pack | ||
_unpack = struct.unpack | ||||
_compress = zlib.compress | ||||
_decompress = zlib.decompress | ||||
|
r21809 | # Some code below makes tuples directly because it's more convenient. However, | ||
# code outside this module should always use dirstatetuple. | ||||
def dirstatetuple(*x): | ||||
# x is a tuple | ||||
return x | ||||
|
r43346 | |||
|
r43347 | indexformatng = b">Qiiiiii20s12x" | ||
indexfirst = struct.calcsize(b'Q') | ||||
sizeint = struct.calcsize(b'i') | ||||
|
r29133 | indexsize = struct.calcsize(indexformatng) | ||
|
r43346 | |||
|
r29133 | def gettype(q): | ||
return int(q & 0xFFFF) | ||||
|
r7945 | |||
|
r43346 | |||
|
r29133 | def offset_type(offset, type): | ||
|
r31529 | return int(int(offset) << 16 | type) | ||
|
r29133 | |||
|
r43346 | |||
|
r29133 | class BaseIndexObject(object): | ||
r43925 | @util.propertycache | |||
def nodemap(self): | ||||
nodemap = revlogutils.NodeMap({nullid: nullrev}) | ||||
for r in range(0, len(self)): | ||||
n = self[r][7] | ||||
nodemap[n] = r | ||||
return nodemap | ||||
r43933 | def _stripnodes(self, start): | |||
if 'nodemap' in vars(self): | ||||
for r in range(start, len(self)): | ||||
n = self[r][7] | ||||
del self.nodemap[n] | ||||
r43925 | def clearcaches(self): | |||
self.__dict__.pop('nodemap', None) | ||||
|
r29133 | def __len__(self): | ||
|
r38887 | return self._lgt + len(self._extra) | ||
|
r29133 | |||
|
r38886 | def append(self, tup): | ||
r43931 | if 'nodemap' in vars(self): | |||
self.nodemap[tup[7]] = len(self) | ||||
|
r29133 | self._extra.append(tup) | ||
|
r7945 | |||
|
r39251 | def _check_index(self, i): | ||
|
r29133 | if not isinstance(i, int): | ||
|
r43347 | raise TypeError(b"expecting int indexes") | ||
|
r39250 | if i < 0 or i >= len(self): | ||
|
r29133 | raise IndexError | ||
|
r7945 | |||
|
r29133 | def __getitem__(self, i): | ||
|
r39082 | if i == -1: | ||
|
r38883 | return (0, 0, 0, -1, -1, -1, -1, nullid) | ||
|
r39251 | self._check_index(i) | ||
|
r29133 | if i >= self._lgt: | ||
return self._extra[i - self._lgt] | ||||
index = self._calculate_index(i) | ||||
|
r43346 | r = struct.unpack(indexformatng, self._data[index : index + indexsize]) | ||
|
r29133 | if i == 0: | ||
e = list(r) | ||||
type = gettype(e[0]) | ||||
e[0] = offset_type(0, type) | ||||
return tuple(e) | ||||
return r | ||||
|
r43346 | |||
|
r29133 | class IndexObject(BaseIndexObject): | ||
def __init__(self, data): | ||||
assert len(data) % indexsize == 0 | ||||
self._data = data | ||||
self._lgt = len(data) // indexsize | ||||
self._extra = [] | ||||
def _calculate_index(self, i): | ||||
return i * indexsize | ||||
|
r13253 | |||
|
r29133 | def __delitem__(self, i): | ||
|
r34332 | if not isinstance(i, slice) or not i.stop == -1 or i.step is not None: | ||
|
r43347 | raise ValueError(b"deleting slices only supports a:-1 with step 1") | ||
|
r39251 | i = i.start | ||
self._check_index(i) | ||||
r43933 | self._stripnodes(i) | |||
|
r29133 | if i < self._lgt: | ||
|
r43346 | self._data = self._data[: i * indexsize] | ||
|
r29133 | self._lgt = i | ||
self._extra = [] | ||||
else: | ||||
|
r43346 | self._extra = self._extra[: i - self._lgt] | ||
|
r29133 | |||
class InlinedIndexObject(BaseIndexObject): | ||||
def __init__(self, data, inline=0): | ||||
self._data = data | ||||
self._lgt = self._inline_scan(None) | ||||
self._inline_scan(self._lgt) | ||||
self._extra = [] | ||||
|
r7700 | |||
|
r29133 | def _inline_scan(self, lgt): | ||
off = 0 | ||||
if lgt is not None: | ||||
self._offsets = [0] * lgt | ||||
count = 0 | ||||
while off <= len(self._data) - indexsize: | ||||
|
r43346 | (s,) = struct.unpack( | ||
|
r43347 | b'>i', self._data[off + indexfirst : off + sizeint + indexfirst] | ||
|
r43346 | ) | ||
|
r29133 | if lgt is not None: | ||
self._offsets[count] = off | ||||
count += 1 | ||||
off += indexsize + s | ||||
if off != len(self._data): | ||||
|
r43347 | raise ValueError(b"corrupted data") | ||
|
r29133 | return count | ||
|
r14421 | |||
|
r29133 | def __delitem__(self, i): | ||
|
r34332 | if not isinstance(i, slice) or not i.stop == -1 or i.step is not None: | ||
|
r43347 | raise ValueError(b"deleting slices only supports a:-1 with step 1") | ||
|
r39251 | i = i.start | ||
self._check_index(i) | ||||
r43933 | self._stripnodes(i) | |||
|
r29133 | if i < self._lgt: | ||
self._offsets = self._offsets[:i] | ||||
self._lgt = i | ||||
self._extra = [] | ||||
else: | ||||
|
r43346 | self._extra = self._extra[: i - self._lgt] | ||
|
r7700 | |||
|
r29133 | def _calculate_index(self, i): | ||
return self._offsets[i] | ||||
|
r7700 | |||
|
r43346 | |||
|
r29133 | def parse_index2(data, inline): | ||
if not inline: | ||||
return IndexObject(data), None | ||||
return InlinedIndexObject(data, inline), (0, data) | ||||
|
r7700 | |||
|
r43346 | |||
|
r7700 | def parse_dirstate(dmap, copymap, st): | ||
|
r43346 | parents = [st[:20], st[20:40]] | ||
|
r17425 | # dereference fields so they will be local in loop | ||
|
r43347 | format = b">cllll" | ||
|
r7945 | e_size = struct.calcsize(format) | ||
|
r7700 | pos1 = 40 | ||
l = len(st) | ||||
# the inner loop | ||||
while pos1 < l: | ||||
pos2 = pos1 + e_size | ||||
|
r43347 | e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster | ||
|
r7700 | pos1 = pos2 + e[4] | ||
f = st[pos2:pos1] | ||||
|
r43347 | if b'\0' in f: | ||
f, c = f.split(b'\0') | ||||
|
r7700 | copymap[f] = c | ||
dmap[f] = e[:4] | ||||
return parents | ||||
|
r18567 | |||
|
r43346 | |||
|
r18567 | def pack_dirstate(dmap, copymap, pl, now): | ||
now = int(now) | ||||
|
r28861 | cs = stringio() | ||
|
r18567 | write = cs.write | ||
|
r43347 | write(b"".join(pl)) | ||
|
r43376 | for f, e in pycompat.iteritems(dmap): | ||
|
r43347 | if e[0] == b'n' and e[3] == now: | ||
|
r18567 | # The file was last modified "simultaneously" with the current | ||
# write to dirstate (i.e. within the same second for file- | ||||
# systems with a granularity of 1 sec). This commonly happens | ||||
# for at least a couple of files on 'update'. | ||||
# The user could change the file without changing its size | ||||
|
r19652 | # within the same second. Invalidate the file's mtime in | ||
|
r18567 | # dirstate, forcing future 'status' calls to compare the | ||
|
r19652 | # contents of the file if the size is the same. This prevents | ||
# mistakenly treating such files as clean. | ||||
|
r21809 | e = dirstatetuple(e[0], e[1], e[2], -1) | ||
|
r18567 | dmap[f] = e | ||
if f in copymap: | ||||
|
r43347 | f = b"%s\0%s" % (f, copymap[f]) | ||
e = _pack(b">cllll", e[0], e[1], e[2], e[3], len(f)) | ||||
|
r18567 | write(e) | ||
write(f) | ||||
return cs.getvalue() | ||||