##// END OF EJS Templates
osutil: add darwin-only version of os.listdir using cffi
Maciej Fijalkowski -
r29600:7a157639 default
parent child Browse files
Show More
@@ -0,0 +1,101 b''
1 from __future__ import absolute_import
2
3 import cffi
4
5 ffi = cffi.FFI()
6 ffi.set_source("_osutil_cffi", """
7 #include <sys/attr.h>
8 #include <sys/vnode.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <time.h>
12
13 typedef struct val_attrs {
14 uint32_t length;
15 attribute_set_t returned;
16 attrreference_t name_info;
17 fsobj_type_t obj_type;
18 struct timespec mtime;
19 uint32_t accessmask;
20 off_t datalength;
21 } __attribute__((aligned(4), packed)) val_attrs_t;
22 """, include_dirs=['mercurial'])
23 ffi.cdef('''
24
25 typedef uint32_t attrgroup_t;
26
27 typedef struct attrlist {
28 uint16_t bitmapcount; /* number of attr. bit sets in list */
29 uint16_t reserved; /* (to maintain 4-byte alignment) */
30 attrgroup_t commonattr; /* common attribute group */
31 attrgroup_t volattr; /* volume attribute group */
32 attrgroup_t dirattr; /* directory attribute group */
33 attrgroup_t fileattr; /* file attribute group */
34 attrgroup_t forkattr; /* fork attribute group */
35 ...;
36 };
37
38 typedef struct attribute_set {
39 ...;
40 } attribute_set_t;
41
42 typedef struct attrreference {
43 int attr_dataoffset;
44 int attr_length;
45 ...;
46 } attrreference_t;
47
48 typedef struct val_attrs {
49 uint32_t length;
50 attribute_set_t returned;
51 attrreference_t name_info;
52 uint32_t obj_type;
53 struct timespec mtime;
54 uint32_t accessmask;
55 int datalength;
56 ...;
57 } val_attrs_t;
58
59 /* the exact layout of the above struct will be figured out during build time */
60
61 typedef int ... time_t;
62 typedef int ... off_t;
63
64 typedef struct timespec {
65 time_t tv_sec;
66 ...;
67 };
68
69 int getattrlist(const char* path, struct attrlist * attrList, void * attrBuf,
70 size_t attrBufSize, unsigned int options);
71
72 int getattrlistbulk(int dirfd, struct attrlist * attrList, void * attrBuf,
73 size_t attrBufSize, uint64_t options);
74
75 #define ATTR_BIT_MAP_COUNT ...
76 #define ATTR_CMN_NAME ...
77 #define ATTR_CMN_OBJTYPE ...
78 #define ATTR_CMN_MODTIME ...
79 #define ATTR_CMN_ACCESSMASK ...
80 #define ATTR_CMN_ERROR ...
81 #define ATTR_CMN_RETURNED_ATTRS ...
82 #define ATTR_FILE_DATALENGTH ...
83
84 #define VREG ...
85 #define VDIR ...
86 #define VLNK ...
87 #define VBLK ...
88 #define VCHR ...
89 #define VFIFO ...
90 #define VSOCK ...
91
92 #define S_IFMT ...
93
94 int open(const char *path, int oflag, int perm);
95 int close(int);
96
97 #define O_RDONLY ...
98 ''')
99
100 if __name__ == '__main__':
101 ffi.compile()
@@ -14,6 +14,10 b' import socket'
14 14 import stat as statmod
15 15 import sys
16 16
17 from . import policy
18 modulepolicy = policy.policy
19 policynocffi = policy.policynocffi
20
17 21 def _mode_to_kind(mode):
18 22 if statmod.S_ISREG(mode):
19 23 return statmod.S_IFREG
@@ -31,7 +35,7 b' def _mode_to_kind(mode):'
31 35 return statmod.S_IFSOCK
32 36 return mode
33 37
34 def listdir(path, stat=False, skip=None):
38 def listdirpure(path, stat=False, skip=None):
35 39 '''listdir(path, stat=False) -> list_of_tuples
36 40
37 41 Return a sorted list containing information about the entries
@@ -61,6 +65,95 b' def listdir(path, stat=False, skip=None)'
61 65 result.append((fn, _mode_to_kind(st.st_mode)))
62 66 return result
63 67
68 ffi = None
69 if modulepolicy not in policynocffi and sys.platform == 'darwin':
70 try:
71 from _osutil_cffi import ffi, lib
72 except ImportError:
73 if modulepolicy == 'cffi': # strict cffi import
74 raise
75
76 if sys.platform == 'darwin' and ffi is not None:
77 listdir_batch_size = 4096
78 # tweakable number, only affects performance, which chunks
79 # of bytes do we get back from getattrlistbulk
80
81 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
82
83 attrkinds[lib.VREG] = statmod.S_IFREG
84 attrkinds[lib.VDIR] = statmod.S_IFDIR
85 attrkinds[lib.VLNK] = statmod.S_IFLNK
86 attrkinds[lib.VBLK] = statmod.S_IFBLK
87 attrkinds[lib.VCHR] = statmod.S_IFCHR
88 attrkinds[lib.VFIFO] = statmod.S_IFIFO
89 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
90
91 class stat_res(object):
92 def __init__(self, st_mode, st_mtime, st_size):
93 self.st_mode = st_mode
94 self.st_mtime = st_mtime
95 self.st_size = st_size
96
97 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
98 buf = ffi.new("char[]", listdir_batch_size)
99
100 def listdirinternal(dfd, req, stat, skip):
101 ret = []
102 while True:
103 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
104 if r == 0:
105 break
106 if r == -1:
107 raise OSError(ffi.errno, os.strerror(ffi.errno))
108 cur = ffi.cast("val_attrs_t*", buf)
109 for i in range(r):
110 lgt = cur.length
111 assert lgt == ffi.cast('uint32_t*', cur)[0]
112 ofs = cur.name_info.attr_dataoffset
113 str_lgt = cur.name_info.attr_length
114 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
115 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
116 str_lgt - 1))
117 tp = attrkinds[cur.obj_type]
118 if name == "." or name == "..":
119 continue
120 if skip == name and tp == statmod.S_ISDIR:
121 return []
122 if stat:
123 mtime = cur.time.tv_sec
124 mode = (cur.accessmask & ~lib.S_IFMT)| tp
125 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
126 st_size=cur.datalength)))
127 else:
128 ret.append((name, tp))
129 cur += lgt
130 return ret
131
132 def listdir(path, stat=False, skip=None):
133 req = ffi.new("struct attrlist*")
134 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
135 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
136 lib.ATTR_CMN_NAME |
137 lib.ATTR_CMN_OBJTYPE |
138 lib.ATTR_CMN_ACCESSMASK |
139 lib.ATTR_CMN_MODTIME)
140 req.fileattr = lib.ATTR_FILE_DATALENGTH
141 dfd = lib.open(path, lib.O_RDONLY, 0)
142 if dfd == -1:
143 raise OSError(ffi.errno, os.strerror(ffi.errno))
144
145 try:
146 ret = listdirinternal(dfd, req, stat, skip)
147 finally:
148 try:
149 lib.close(dfd)
150 except BaseException:
151 pass # we ignore all the errors from closing, not
152 # much we can do about that
153 return ret
154 else:
155 listdir = listdirpure
156
64 157 if os.name != 'nt':
65 158 posixfile = open
66 159
@@ -320,6 +320,9 b' class hgbuildpy(build_py):'
320 320 elif self.distribution.cffi:
321 321 exts = []
322 322 # cffi modules go here
323 if sys.platform == 'darwin':
324 import setup_osutil_cffi
325 exts.append(setup_osutil_cffi.ffi.distutils_extension())
323 326 self.distribution.ext_modules = exts
324 327 else:
325 328 h = os.path.join(get_python_inc(), 'Python.h')
General Comments 0
You need to be logged in to leave comments. Login now