# HG changeset patch # User Yuya Nishihara # Date 1493727331 -32400 # Node ID 0e8b0b9a7acc8b0de7b73172489f86947c3cdf71 # Parent 2e431fb98c6b539be848d91a4a379023b26aad58 cffi: split modules from pure The copyright lines are updated per change history. cffi/osutil.py isn't tested since I have no access to OS X machine right now, sorry. diff --git a/mercurial/cffi/base85.py b/mercurial/cffi/base85.py new file mode 100644 --- /dev/null +++ b/mercurial/cffi/base85.py @@ -0,0 +1,10 @@ +# base85.py: pure python base85 codec +# +# Copyright (C) 2009 Brendan Cully +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from ..pure.base85 import * diff --git a/mercurial/pure/bdiff.py b/mercurial/cffi/bdiff.py copy from mercurial/pure/bdiff.py copy to mercurial/cffi/bdiff.py --- a/mercurial/pure/bdiff.py +++ b/mercurial/cffi/bdiff.py @@ -1,106 +1,22 @@ -# bdiff.py - Python implementation of bdiff.c +# bdiff.py - CFFI implementation of bdiff.c # -# Copyright 2009 Matt Mackall and others +# Copyright 2016 Maciej Fijalkowski # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import -import difflib -import re import struct -from .. import policy -policynocffi = policy.policynocffi -modulepolicy = policy.policy - -def splitnewlines(text): - '''like str.splitlines, but only split on newlines.''' - lines = [l + '\n' for l in text.split('\n')] - if lines: - if lines[-1] == '\n': - lines.pop() - else: - lines[-1] = lines[-1][:-1] - return lines - -def _normalizeblocks(a, b, blocks): - prev = None - r = [] - for curr in blocks: - if prev is None: - prev = curr - continue - shift = 0 - - a1, b1, l1 = prev - a1end = a1 + l1 - b1end = b1 + l1 - - a2, b2, l2 = curr - a2end = a2 + l2 - b2end = b2 + l2 - if a1end == a2: - while (a1end + shift < a2end and - a[a1end + shift] == b[b1end + shift]): - shift += 1 - elif b1end == b2: - while (b1end + shift < b2end and - a[a1end + shift] == b[b1end + shift]): - shift += 1 - r.append((a1, b1, l1 + shift)) - prev = a2 + shift, b2 + shift, l2 - shift - r.append(prev) - return r +from ..pure.bdiff import * +from . import _bdiff -def bdiff(a, b): - a = bytes(a).splitlines(True) - b = bytes(b).splitlines(True) - - if not a: - s = "".join(b) - return s and (struct.pack(">lll", 0, 0, len(s)) + s) - - bin = [] - p = [0] - for i in a: p.append(p[-1] + len(i)) - - d = difflib.SequenceMatcher(None, a, b).get_matching_blocks() - d = _normalizeblocks(a, b, d) - la = 0 - lb = 0 - for am, bm, size in d: - s = "".join(b[lb:bm]) - if am > la or s: - bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s) - la = am + size - lb = bm + size +ffi = _bdiff.ffi +lib = _bdiff.lib - return "".join(bin) - -def blocks(a, b): - an = splitnewlines(a) - bn = splitnewlines(b) - d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks() - d = _normalizeblocks(an, bn, d) - return [(i, i + n, j, j + n) for (i, j, n) in d] - -def fixws(text, allws): - if allws: - text = re.sub('[ \t\r]+', '', text) - else: - text = re.sub('[ \t\r]+', ' ', text) - text = text.replace(' \n', '\n') - return text - -if modulepolicy not in policynocffi: - try: - from ..cffi._bdiff import ffi, lib - except ImportError: - if modulepolicy == 'cffi': # strict cffi import - raise - else: +if True: + if True: def blocks(sa, sb): a = ffi.new("struct bdiff_line**") b = ffi.new("struct bdiff_line**") diff --git a/mercurial/cffi/diffhelpers.py b/mercurial/cffi/diffhelpers.py new file mode 100644 --- /dev/null +++ b/mercurial/cffi/diffhelpers.py @@ -0,0 +1,10 @@ +# diffhelpers.py - pure Python implementation of diffhelpers.c +# +# Copyright 2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from ..pure.diffhelpers import * diff --git a/mercurial/pure/mpatch.py b/mercurial/cffi/mpatch.py copy from mercurial/pure/mpatch.py copy to mercurial/cffi/mpatch.py --- a/mercurial/pure/mpatch.py +++ b/mercurial/cffi/mpatch.py @@ -1,140 +1,21 @@ -# mpatch.py - Python implementation of mpatch.c +# mpatch.py - CFFI implementation of mpatch.c # -# Copyright 2009 Matt Mackall and others +# Copyright 2016 Maciej Fijalkowski # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import -import struct - -from .. import policy, pycompat -stringio = pycompat.stringio -modulepolicy = policy.policy -policynocffi = policy.policynocffi - -class mpatchError(Exception): - """error raised when a delta cannot be decoded - """ - -# This attempts to apply a series of patches in time proportional to -# the total size of the patches, rather than patches * len(text). This -# means rather than shuffling strings around, we shuffle around -# pointers to fragments with fragment lists. -# -# When the fragment lists get too long, we collapse them. To do this -# efficiently, we do all our operations inside a buffer created by -# mmap and simply use memmove. This avoids creating a bunch of large -# temporary string buffers. - -def _pull(dst, src, l): # pull l bytes from src - while l: - f = src.pop() - if f[0] > l: # do we need to split? - src.append((f[0] - l, f[1] + l)) - dst.append((l, f[1])) - return - dst.append(f) - l -= f[0] - -def _move(m, dest, src, count): - """move count bytes from src to dest - - The file pointer is left at the end of dest. - """ - m.seek(src) - buf = m.read(count) - m.seek(dest) - m.write(buf) - -def _collect(m, buf, list): - start = buf - for l, p in reversed(list): - _move(m, buf, p, l) - buf += l - return (buf - start, start) - -def patches(a, bins): - if not bins: - return a - - plens = [len(x) for x in bins] - pl = sum(plens) - bl = len(a) + pl - tl = bl + bl + pl # enough for the patches and two working texts - b1, b2 = 0, bl - - if not tl: - return a - - m = stringio() +from ..pure.mpatch import * +from ..pure.mpatch import mpatchError # silence pyflakes +from . import _mpatch - # load our original text - m.write(a) - frags = [(len(a), b1)] - - # copy all the patches into our segment so we can memmove from them - pos = b2 + bl - m.seek(pos) - for p in bins: m.write(p) - - for plen in plens: - # if our list gets too long, execute it - if len(frags) > 128: - b2, b1 = b1, b2 - frags = [_collect(m, b1, frags)] - - new = [] - end = pos + plen - last = 0 - while pos < end: - m.seek(pos) - try: - p1, p2, l = struct.unpack(">lll", m.read(12)) - except struct.error: - raise mpatchError("patch cannot be decoded") - _pull(new, frags, p1 - last) # what didn't change - _pull([], frags, p2 - p1) # what got deleted - new.append((l, pos + 12)) # what got added - pos += l + 12 - last = p2 - frags.extend(reversed(new)) # what was left at the end - - t = _collect(m, b2, frags) +ffi = _mpatch.ffi +lib = _mpatch.lib - m.seek(t[1]) - return m.read(t[0]) - -def patchedsize(orig, delta): - outlen, last, bin = 0, 0, 0 - binend = len(delta) - data = 12 - - while data <= binend: - decode = delta[bin:bin + 12] - start, end, length = struct.unpack(">lll", decode) - if start > end: - break - bin = data + length - data = bin + 12 - outlen += start - last - last = end - outlen += length - - if bin != binend: - raise mpatchError("patch cannot be decoded") - - outlen += orig - last - return outlen - -if modulepolicy not in policynocffi: - try: - from ..cffi._mpatch import ffi, lib - except ImportError: - if modulepolicy == 'cffi': # strict cffi import - raise - else: +if True: + if True: @ffi.def_extern() def cffi_get_next_item(arg, pos): all, bins = ffi.from_handle(arg) diff --git a/mercurial/pure/osutil.py b/mercurial/cffi/osutil.py copy from mercurial/pure/osutil.py copy to mercurial/cffi/osutil.py --- a/mercurial/pure/osutil.py +++ b/mercurial/cffi/osutil.py @@ -1,82 +1,27 @@ -# osutil.py - pure Python version of osutil.c +# osutil.py - CFFI version of osutil.c # -# Copyright 2009 Matt Mackall and others +# Copyright 2016 Maciej Fijalkowski # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import -import ctypes -import ctypes.util import os -import socket import stat as statmod +from ..pure.osutil import * + from .. import ( - policy, pycompat, ) -modulepolicy = policy.policy -policynocffi = policy.policynocffi - -def _mode_to_kind(mode): - if statmod.S_ISREG(mode): - return statmod.S_IFREG - if statmod.S_ISDIR(mode): - return statmod.S_IFDIR - if statmod.S_ISLNK(mode): - return statmod.S_IFLNK - if statmod.S_ISBLK(mode): - return statmod.S_IFBLK - if statmod.S_ISCHR(mode): - return statmod.S_IFCHR - if statmod.S_ISFIFO(mode): - return statmod.S_IFIFO - if statmod.S_ISSOCK(mode): - return statmod.S_IFSOCK - return mode - -def listdirpure(path, stat=False, skip=None): - '''listdir(path, stat=False) -> list_of_tuples - - Return a sorted list containing information about the entries - in the directory. - - If stat is True, each element is a 3-tuple: - - (name, type, stat object) +if pycompat.sysplatform == 'darwin': + from . import _osutil - Otherwise, each element is a 2-tuple: + ffi = _osutil.ffi + lib = _osutil.lib - (name, type) - ''' - result = [] - prefix = path - if not prefix.endswith(pycompat.ossep): - prefix += pycompat.ossep - names = os.listdir(path) - names.sort() - for fn in names: - st = os.lstat(prefix + fn) - if fn == skip and statmod.S_ISDIR(st.st_mode): - return [] - if stat: - result.append((fn, _mode_to_kind(st.st_mode), st)) - else: - result.append((fn, _mode_to_kind(st.st_mode))) - return result - -ffi = None -if modulepolicy not in policynocffi and pycompat.sysplatform == 'darwin': - try: - from ..cffi._osutil import ffi, lib - except ImportError: - if modulepolicy == 'cffi': # strict cffi import - raise - -if pycompat.sysplatform == 'darwin' and ffi is not None: listdir_batch_size = 4096 # tweakable number, only affects performance, which chunks # of bytes do we get back from getattrlistbulk @@ -155,211 +100,3 @@ pass # we ignore all the errors from closing, not # much we can do about that return ret -else: - listdir = listdirpure - -if pycompat.osname != 'nt': - posixfile = open - - _SCM_RIGHTS = 0x01 - _socklen_t = ctypes.c_uint - - if pycompat.sysplatform.startswith('linux'): - # socket.h says "the type should be socklen_t but the definition of - # the kernel is incompatible with this." - _cmsg_len_t = ctypes.c_size_t - _msg_controllen_t = ctypes.c_size_t - _msg_iovlen_t = ctypes.c_size_t - else: - _cmsg_len_t = _socklen_t - _msg_controllen_t = _socklen_t - _msg_iovlen_t = ctypes.c_int - - class _iovec(ctypes.Structure): - _fields_ = [ - (u'iov_base', ctypes.c_void_p), - (u'iov_len', ctypes.c_size_t), - ] - - class _msghdr(ctypes.Structure): - _fields_ = [ - (u'msg_name', ctypes.c_void_p), - (u'msg_namelen', _socklen_t), - (u'msg_iov', ctypes.POINTER(_iovec)), - (u'msg_iovlen', _msg_iovlen_t), - (u'msg_control', ctypes.c_void_p), - (u'msg_controllen', _msg_controllen_t), - (u'msg_flags', ctypes.c_int), - ] - - class _cmsghdr(ctypes.Structure): - _fields_ = [ - (u'cmsg_len', _cmsg_len_t), - (u'cmsg_level', ctypes.c_int), - (u'cmsg_type', ctypes.c_int), - (u'cmsg_data', ctypes.c_ubyte * 0), - ] - - _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True) - _recvmsg = getattr(_libc, 'recvmsg', None) - if _recvmsg: - _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long) - _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr), - ctypes.c_int) - else: - # recvmsg isn't always provided by libc; such systems are unsupported - def _recvmsg(sockfd, msg, flags): - raise NotImplementedError('unsupported platform') - - def _CMSG_FIRSTHDR(msgh): - if msgh.msg_controllen < ctypes.sizeof(_cmsghdr): - return - cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr)) - return cmsgptr.contents - - # The pure version is less portable than the native version because the - # handling of socket ancillary data heavily depends on C preprocessor. - # Also, some length fields are wrongly typed in Linux kernel. - def recvfds(sockfd): - """receive list of file descriptors via socket""" - dummy = (ctypes.c_ubyte * 1)() - iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy)) - cbuf = ctypes.create_string_buffer(256) - msgh = _msghdr(None, 0, - ctypes.pointer(iov), 1, - ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf), - 0) - r = _recvmsg(sockfd, ctypes.byref(msgh), 0) - if r < 0: - e = ctypes.get_errno() - raise OSError(e, os.strerror(e)) - # assumes that the first cmsg has fds because it isn't easy to write - # portable CMSG_NXTHDR() with ctypes. - cmsg = _CMSG_FIRSTHDR(msgh) - if not cmsg: - return [] - if (cmsg.cmsg_level != socket.SOL_SOCKET or - cmsg.cmsg_type != _SCM_RIGHTS): - return [] - rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int)) - rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) / - ctypes.sizeof(ctypes.c_int)) - return [rfds[i] for i in xrange(rfdscount)] - -else: - import msvcrt - - _kernel32 = ctypes.windll.kernel32 - - _DWORD = ctypes.c_ulong - _LPCSTR = _LPSTR = ctypes.c_char_p - _HANDLE = ctypes.c_void_p - - _INVALID_HANDLE_VALUE = _HANDLE(-1).value - - # CreateFile - _FILE_SHARE_READ = 0x00000001 - _FILE_SHARE_WRITE = 0x00000002 - _FILE_SHARE_DELETE = 0x00000004 - - _CREATE_ALWAYS = 2 - _OPEN_EXISTING = 3 - _OPEN_ALWAYS = 4 - - _GENERIC_READ = 0x80000000 - _GENERIC_WRITE = 0x40000000 - - _FILE_ATTRIBUTE_NORMAL = 0x80 - - # open_osfhandle flags - _O_RDONLY = 0x0000 - _O_RDWR = 0x0002 - _O_APPEND = 0x0008 - - _O_TEXT = 0x4000 - _O_BINARY = 0x8000 - - # types of parameters of C functions used (required by pypy) - - _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p, - _DWORD, _DWORD, _HANDLE] - _kernel32.CreateFileA.restype = _HANDLE - - def _raiseioerror(name): - err = ctypes.WinError() - raise IOError(err.errno, '%s: %s' % (name, err.strerror)) - - class posixfile(object): - '''a file object aiming for POSIX-like semantics - - CPython's open() returns a file that was opened *without* setting the - _FILE_SHARE_DELETE flag, which causes rename and unlink to abort. - This even happens if any hardlinked copy of the file is in open state. - We set _FILE_SHARE_DELETE here, so files opened with posixfile can be - renamed and deleted while they are held open. - Note that if a file opened with posixfile is unlinked, the file - remains but cannot be opened again or be recreated under the same name, - until all reading processes have closed the file.''' - - def __init__(self, name, mode='r', bufsize=-1): - if 'b' in mode: - flags = _O_BINARY - else: - flags = _O_TEXT - - m0 = mode[0] - if m0 == 'r' and '+' not in mode: - flags |= _O_RDONLY - access = _GENERIC_READ - else: - # work around http://support.microsoft.com/kb/899149 and - # set _O_RDWR for 'w' and 'a', even if mode has no '+' - flags |= _O_RDWR - access = _GENERIC_READ | _GENERIC_WRITE - - if m0 == 'r': - creation = _OPEN_EXISTING - elif m0 == 'w': - creation = _CREATE_ALWAYS - elif m0 == 'a': - creation = _OPEN_ALWAYS - flags |= _O_APPEND - else: - raise ValueError("invalid mode: %s" % mode) - - fh = _kernel32.CreateFileA(name, access, - _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE, - None, creation, _FILE_ATTRIBUTE_NORMAL, None) - if fh == _INVALID_HANDLE_VALUE: - _raiseioerror(name) - - fd = msvcrt.open_osfhandle(fh, flags) - if fd == -1: - _kernel32.CloseHandle(fh) - _raiseioerror(name) - - f = os.fdopen(fd, pycompat.sysstr(mode), bufsize) - # unfortunately, f.name is '' at this point -- so we store - # the name on this wrapper. We cannot just assign to f.name, - # because that attribute is read-only. - object.__setattr__(self, r'name', name) - object.__setattr__(self, r'_file', f) - - def __iter__(self): - return self._file - - def __getattr__(self, name): - return getattr(self._file, name) - - def __setattr__(self, name, value): - '''mimics the read-only attributes of Python file objects - by raising 'TypeError: readonly attribute' if someone tries: - f = posixfile('foo.txt') - f.name = 'bla' ''' - return self._file.__setattr__(name, value) - - def __enter__(self): - return self._file.__enter__() - - def __exit__(self, exc_type, exc_value, exc_tb): - return self._file.__exit__(exc_type, exc_value, exc_tb) diff --git a/mercurial/cffi/parsers.py b/mercurial/cffi/parsers.py new file mode 100644 --- /dev/null +++ b/mercurial/cffi/parsers.py @@ -0,0 +1,10 @@ +# parsers.py - Python implementation of parsers.c +# +# Copyright 2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from ..pure.parsers import * diff --git a/mercurial/policy.py b/mercurial/policy.py --- a/mercurial/policy.py +++ b/mercurial/policy.py @@ -28,8 +28,8 @@ # policy: (versioned package, pure package) b'c': (r'cext', None), b'allow': (r'cext', r'pure'), - b'cffi': (None, r'pure'), # TODO: (r'cffi', None) - b'cffi-allow': (None, r'pure'), # TODO: (r'cffi', r'pure') + b'cffi': (r'cffi', None), + b'cffi-allow': (r'cffi', r'pure'), b'py': (None, r'pure'), } diff --git a/mercurial/pure/bdiff.py b/mercurial/pure/bdiff.py --- a/mercurial/pure/bdiff.py +++ b/mercurial/pure/bdiff.py @@ -11,10 +11,6 @@ import re import struct -from .. import policy -policynocffi = policy.policynocffi -modulepolicy = policy.policy - def splitnewlines(text): '''like str.splitlines, but only split on newlines.''' lines = [l + '\n' for l in text.split('\n')] @@ -93,70 +89,3 @@ text = re.sub('[ \t\r]+', ' ', text) text = text.replace(' \n', '\n') return text - -if modulepolicy not in policynocffi: - try: - from ..cffi._bdiff import ffi, lib - except ImportError: - if modulepolicy == 'cffi': # strict cffi import - raise - else: - def blocks(sa, sb): - a = ffi.new("struct bdiff_line**") - b = ffi.new("struct bdiff_line**") - ac = ffi.new("char[]", str(sa)) - bc = ffi.new("char[]", str(sb)) - l = ffi.new("struct bdiff_hunk*") - try: - an = lib.bdiff_splitlines(ac, len(sa), a) - bn = lib.bdiff_splitlines(bc, len(sb), b) - if not a[0] or not b[0]: - raise MemoryError - count = lib.bdiff_diff(a[0], an, b[0], bn, l) - if count < 0: - raise MemoryError - rl = [None] * count - h = l.next - i = 0 - while h: - rl[i] = (h.a1, h.a2, h.b1, h.b2) - h = h.next - i += 1 - finally: - lib.free(a[0]) - lib.free(b[0]) - lib.bdiff_freehunks(l.next) - return rl - - def bdiff(sa, sb): - a = ffi.new("struct bdiff_line**") - b = ffi.new("struct bdiff_line**") - ac = ffi.new("char[]", str(sa)) - bc = ffi.new("char[]", str(sb)) - l = ffi.new("struct bdiff_hunk*") - try: - an = lib.bdiff_splitlines(ac, len(sa), a) - bn = lib.bdiff_splitlines(bc, len(sb), b) - if not a[0] or not b[0]: - raise MemoryError - count = lib.bdiff_diff(a[0], an, b[0], bn, l) - if count < 0: - raise MemoryError - rl = [] - h = l.next - la = lb = 0 - while h: - if h.a1 != la or h.b1 != lb: - lgt = (b[0] + h.b1).l - (b[0] + lb).l - rl.append(struct.pack(">lll", (a[0] + la).l - a[0].l, - (a[0] + h.a1).l - a[0].l, lgt)) - rl.append(str(ffi.buffer((b[0] + lb).l, lgt))) - la = h.a2 - lb = h.b2 - h = h.next - - finally: - lib.free(a[0]) - lib.free(b[0]) - lib.bdiff_freehunks(l.next) - return "".join(rl) diff --git a/mercurial/pure/mpatch.py b/mercurial/pure/mpatch.py --- a/mercurial/pure/mpatch.py +++ b/mercurial/pure/mpatch.py @@ -9,10 +9,8 @@ import struct -from .. import policy, pycompat +from .. import pycompat stringio = pycompat.stringio -modulepolicy = policy.policy -policynocffi = policy.policynocffi class mpatchError(Exception): """error raised when a delta cannot be decoded @@ -127,43 +125,3 @@ outlen += orig - last return outlen - -if modulepolicy not in policynocffi: - try: - from ..cffi._mpatch import ffi, lib - except ImportError: - if modulepolicy == 'cffi': # strict cffi import - raise - else: - @ffi.def_extern() - def cffi_get_next_item(arg, pos): - all, bins = ffi.from_handle(arg) - container = ffi.new("struct mpatch_flist*[1]") - to_pass = ffi.new("char[]", str(bins[pos])) - all.append(to_pass) - r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container) - if r < 0: - return ffi.NULL - return container[0] - - def patches(text, bins): - lgt = len(bins) - all = [] - if not lgt: - return text - arg = (all, bins) - patch = lib.mpatch_fold(ffi.new_handle(arg), - lib.cffi_get_next_item, 0, lgt) - if not patch: - raise mpatchError("cannot decode chunk") - outlen = lib.mpatch_calcsize(len(text), patch) - if outlen < 0: - lib.mpatch_lfree(patch) - raise mpatchError("inconsistency detected") - buf = ffi.new("char[]", outlen) - if lib.mpatch_apply(buf, text, len(text), patch) < 0: - lib.mpatch_lfree(patch) - raise mpatchError("error applying patches") - res = ffi.buffer(buf, outlen)[:] - lib.mpatch_lfree(patch) - return res diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py --- a/mercurial/pure/osutil.py +++ b/mercurial/pure/osutil.py @@ -14,13 +14,9 @@ import stat as statmod from .. import ( - policy, pycompat, ) -modulepolicy = policy.policy -policynocffi = policy.policynocffi - def _mode_to_kind(mode): if statmod.S_ISREG(mode): return statmod.S_IFREG @@ -38,7 +34,7 @@ return statmod.S_IFSOCK return mode -def listdirpure(path, stat=False, skip=None): +def listdir(path, stat=False, skip=None): '''listdir(path, stat=False) -> list_of_tuples Return a sorted list containing information about the entries @@ -68,96 +64,6 @@ result.append((fn, _mode_to_kind(st.st_mode))) return result -ffi = None -if modulepolicy not in policynocffi and pycompat.sysplatform == 'darwin': - try: - from ..cffi._osutil import ffi, lib - except ImportError: - if modulepolicy == 'cffi': # strict cffi import - raise - -if pycompat.sysplatform == 'darwin' and ffi is not None: - listdir_batch_size = 4096 - # tweakable number, only affects performance, which chunks - # of bytes do we get back from getattrlistbulk - - attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty - - attrkinds[lib.VREG] = statmod.S_IFREG - attrkinds[lib.VDIR] = statmod.S_IFDIR - attrkinds[lib.VLNK] = statmod.S_IFLNK - attrkinds[lib.VBLK] = statmod.S_IFBLK - attrkinds[lib.VCHR] = statmod.S_IFCHR - attrkinds[lib.VFIFO] = statmod.S_IFIFO - attrkinds[lib.VSOCK] = statmod.S_IFSOCK - - class stat_res(object): - def __init__(self, st_mode, st_mtime, st_size): - self.st_mode = st_mode - self.st_mtime = st_mtime - self.st_size = st_size - - tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec") - buf = ffi.new("char[]", listdir_batch_size) - - def listdirinternal(dfd, req, stat, skip): - ret = [] - while True: - r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0) - if r == 0: - break - if r == -1: - raise OSError(ffi.errno, os.strerror(ffi.errno)) - cur = ffi.cast("val_attrs_t*", buf) - for i in range(r): - lgt = cur.length - assert lgt == ffi.cast('uint32_t*', cur)[0] - ofs = cur.name_info.attr_dataoffset - str_lgt = cur.name_info.attr_length - base_ofs = ffi.offsetof('val_attrs_t', 'name_info') - name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs, - str_lgt - 1)) - tp = attrkinds[cur.obj_type] - if name == "." or name == "..": - continue - if skip == name and tp == statmod.S_ISDIR: - return [] - if stat: - mtime = cur.mtime.tv_sec - mode = (cur.accessmask & ~lib.S_IFMT)| tp - ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime, - st_size=cur.datalength))) - else: - ret.append((name, tp)) - cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur)) - + lgt) - return ret - - def listdir(path, stat=False, skip=None): - req = ffi.new("struct attrlist*") - req.bitmapcount = lib.ATTR_BIT_MAP_COUNT - req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS | - lib.ATTR_CMN_NAME | - lib.ATTR_CMN_OBJTYPE | - lib.ATTR_CMN_ACCESSMASK | - lib.ATTR_CMN_MODTIME) - req.fileattr = lib.ATTR_FILE_DATALENGTH - dfd = lib.open(path, lib.O_RDONLY, 0) - if dfd == -1: - raise OSError(ffi.errno, os.strerror(ffi.errno)) - - try: - ret = listdirinternal(dfd, req, stat, skip) - finally: - try: - lib.close(dfd) - except BaseException: - pass # we ignore all the errors from closing, not - # much we can do about that - return ret -else: - listdir = listdirpure - if pycompat.osname != 'nt': posixfile = open