Branch data Line data Source code
1 : : /*
2 : : / Author: Sam Rushing <rushing@nightmare.com>
3 : : / Hacked for Unix by AMK
4 : : / $Id$
5 : :
6 : : / Modified to support mmap with offset - to map a 'window' of a file
7 : : / Author: Yotam Medini yotamm@mellanox.co.il
8 : : /
9 : : / mmapmodule.cpp -- map a view of a file into memory
10 : : /
11 : : / todo: need permission flags, perhaps a 'chsize' analog
12 : : / not all functions check range yet!!!
13 : : /
14 : : /
15 : : / This version of mmapmodule.c has been changed significantly
16 : : / from the original mmapfile.c on which it was based.
17 : : / The original version of mmapfile is maintained by Sam at
18 : : / ftp://squirl.nightmare.com/pub/python/python-ext.
19 : : */
20 : :
21 : : #ifndef Py_BUILD_CORE_BUILTIN
22 : : # define Py_BUILD_CORE_MODULE 1
23 : : #endif
24 : :
25 : : #define PY_SSIZE_T_CLEAN
26 : : #include <Python.h>
27 : : #include "pycore_bytesobject.h" // _PyBytes_Find()
28 : : #include "pycore_fileutils.h" // _Py_stat_struct
29 : : #include "structmember.h" // PyMemberDef
30 : : #include <stddef.h> // offsetof()
31 : :
32 : : #ifndef MS_WINDOWS
33 : : #define UNIX
34 : : # ifdef HAVE_FCNTL_H
35 : : # include <fcntl.h>
36 : : # endif /* HAVE_FCNTL_H */
37 : : #endif
38 : :
39 : : #ifdef MS_WINDOWS
40 : : #include <windows.h>
41 : : static int
42 : : my_getpagesize(void)
43 : : {
44 : : SYSTEM_INFO si;
45 : : GetSystemInfo(&si);
46 : : return si.dwPageSize;
47 : : }
48 : :
49 : : static int
50 : : my_getallocationgranularity (void)
51 : : {
52 : :
53 : : SYSTEM_INFO si;
54 : : GetSystemInfo(&si);
55 : : return si.dwAllocationGranularity;
56 : : }
57 : :
58 : : #endif
59 : :
60 : : #ifdef UNIX
61 : : #include <sys/mman.h>
62 : : #include <sys/stat.h>
63 : :
64 : : #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
65 : : static int
66 : 682 : my_getpagesize(void)
67 : : {
68 : 682 : return sysconf(_SC_PAGESIZE);
69 : : }
70 : :
71 : : #define my_getallocationgranularity my_getpagesize
72 : : #else
73 : : #define my_getpagesize getpagesize
74 : : #endif
75 : :
76 : : #endif /* UNIX */
77 : :
78 : : #include <string.h>
79 : :
80 : : #ifdef HAVE_SYS_TYPES_H
81 : : #include <sys/types.h>
82 : : #endif /* HAVE_SYS_TYPES_H */
83 : :
84 : : /* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
85 : : #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
86 : : # define MAP_ANONYMOUS MAP_ANON
87 : : #endif
88 : :
89 : : typedef enum
90 : : {
91 : : ACCESS_DEFAULT,
92 : : ACCESS_READ,
93 : : ACCESS_WRITE,
94 : : ACCESS_COPY
95 : : } access_mode;
96 : :
97 : : typedef struct {
98 : : PyObject_HEAD
99 : : char * data;
100 : : Py_ssize_t size;
101 : : Py_ssize_t pos; /* relative to offset */
102 : : #ifdef MS_WINDOWS
103 : : long long offset;
104 : : #else
105 : : off_t offset;
106 : : #endif
107 : : Py_ssize_t exports;
108 : :
109 : : #ifdef MS_WINDOWS
110 : : HANDLE map_handle;
111 : : HANDLE file_handle;
112 : : char * tagname;
113 : : #endif
114 : :
115 : : #ifdef UNIX
116 : : int fd;
117 : : #endif
118 : :
119 : : PyObject *weakreflist;
120 : : access_mode access;
121 : : } mmap_object;
122 : :
123 : : static int
124 : 1220 : mmap_object_traverse(mmap_object *m_obj, visitproc visit, void *arg)
125 : : {
126 [ + - - + ]: 1220 : Py_VISIT(Py_TYPE(m_obj));
127 : 1220 : return 0;
128 : : }
129 : :
130 : : static void
131 : 891 : mmap_object_dealloc(mmap_object *m_obj)
132 : : {
133 : 891 : PyTypeObject *tp = Py_TYPE(m_obj);
134 : 891 : PyObject_GC_UnTrack(m_obj);
135 : :
136 : : #ifdef MS_WINDOWS
137 : : Py_BEGIN_ALLOW_THREADS
138 : : if (m_obj->data != NULL)
139 : : UnmapViewOfFile (m_obj->data);
140 : : if (m_obj->map_handle != NULL)
141 : : CloseHandle (m_obj->map_handle);
142 : : if (m_obj->file_handle != INVALID_HANDLE_VALUE)
143 : : CloseHandle (m_obj->file_handle);
144 : : Py_END_ALLOW_THREADS
145 : : if (m_obj->tagname)
146 : : PyMem_Free(m_obj->tagname);
147 : : #endif /* MS_WINDOWS */
148 : :
149 : : #ifdef UNIX
150 : 891 : Py_BEGIN_ALLOW_THREADS
151 [ + + ]: 891 : if (m_obj->fd >= 0)
152 : 130 : (void) close(m_obj->fd);
153 [ + + ]: 891 : if (m_obj->data!=NULL) {
154 : 140 : munmap(m_obj->data, m_obj->size);
155 : : }
156 : 891 : Py_END_ALLOW_THREADS
157 : : #endif /* UNIX */
158 : :
159 [ + + ]: 891 : if (m_obj->weakreflist != NULL)
160 : 1 : PyObject_ClearWeakRefs((PyObject *) m_obj);
161 : :
162 : 891 : tp->tp_free(m_obj);
163 : 891 : Py_DECREF(tp);
164 : 891 : }
165 : :
166 : : static PyObject *
167 : 751 : mmap_close_method(mmap_object *self, PyObject *unused)
168 : : {
169 [ - + ]: 751 : if (self->exports > 0) {
170 : 0 : PyErr_SetString(PyExc_BufferError, "cannot close "\
171 : : "exported pointers exist");
172 : 0 : return NULL;
173 : : }
174 : : #ifdef MS_WINDOWS
175 : : /* For each resource we maintain, we need to check
176 : : the value is valid, and if so, free the resource
177 : : and set the member value to an invalid value so
178 : : the dealloc does not attempt to resource clearing
179 : : again.
180 : : TODO - should we check for errors in the close operations???
181 : : */
182 : : HANDLE map_handle = self->map_handle;
183 : : HANDLE file_handle = self->file_handle;
184 : : char *data = self->data;
185 : : self->map_handle = NULL;
186 : : self->file_handle = INVALID_HANDLE_VALUE;
187 : : self->data = NULL;
188 : : Py_BEGIN_ALLOW_THREADS
189 : : if (data != NULL) {
190 : : UnmapViewOfFile(data);
191 : : }
192 : : if (map_handle != NULL) {
193 : : CloseHandle(map_handle);
194 : : }
195 : : if (file_handle != INVALID_HANDLE_VALUE) {
196 : : CloseHandle(file_handle);
197 : : }
198 : : Py_END_ALLOW_THREADS
199 : : #endif /* MS_WINDOWS */
200 : :
201 : : #ifdef UNIX
202 : 751 : int fd = self->fd;
203 : 751 : char *data = self->data;
204 : 751 : self->fd = -1;
205 : 751 : self->data = NULL;
206 : 751 : Py_BEGIN_ALLOW_THREADS
207 [ + + ]: 751 : if (0 <= fd)
208 : 355 : (void) close(fd);
209 [ + + ]: 751 : if (data != NULL) {
210 : 750 : munmap(data, self->size);
211 : : }
212 : 751 : Py_END_ALLOW_THREADS
213 : : #endif
214 : :
215 : 751 : Py_RETURN_NONE;
216 : : }
217 : :
218 : : #ifdef MS_WINDOWS
219 : : #define CHECK_VALID(err) \
220 : : do { \
221 : : if (self->map_handle == NULL) { \
222 : : PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
223 : : return err; \
224 : : } \
225 : : } while (0)
226 : : #endif /* MS_WINDOWS */
227 : :
228 : : #ifdef UNIX
229 : : #define CHECK_VALID(err) \
230 : : do { \
231 : : if (self->data == NULL) { \
232 : : PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
233 : : return err; \
234 : : } \
235 : : } while (0)
236 : : #endif /* UNIX */
237 : :
238 : : static PyObject *
239 : 15 : mmap_read_byte_method(mmap_object *self,
240 : : PyObject *unused)
241 : : {
242 [ - + ]: 15 : CHECK_VALID(NULL);
243 [ + + ]: 15 : if (self->pos >= self->size) {
244 : 2 : PyErr_SetString(PyExc_ValueError, "read byte out of range");
245 : 2 : return NULL;
246 : : }
247 : 13 : return PyLong_FromLong((unsigned char)self->data[self->pos++]);
248 : : }
249 : :
250 : : static PyObject *
251 : 0 : mmap_read_line_method(mmap_object *self,
252 : : PyObject *unused)
253 : : {
254 : : Py_ssize_t remaining;
255 : : char *start, *eol;
256 : : PyObject *result;
257 : :
258 [ # # ]: 0 : CHECK_VALID(NULL);
259 : :
260 [ # # ]: 0 : remaining = (self->pos < self->size) ? self->size - self->pos : 0;
261 [ # # ]: 0 : if (!remaining)
262 : 0 : return PyBytes_FromString("");
263 : 0 : start = self->data + self->pos;
264 : 0 : eol = memchr(start, '\n', remaining);
265 [ # # ]: 0 : if (!eol)
266 : 0 : eol = self->data + self->size;
267 : : else
268 : 0 : ++eol; /* advance past newline */
269 : 0 : result = PyBytes_FromStringAndSize(start, (eol - start));
270 : 0 : self->pos += (eol - start);
271 : 0 : return result;
272 : : }
273 : :
274 : : static PyObject *
275 : 14 : mmap_read_method(mmap_object *self,
276 : : PyObject *args)
277 : : {
278 : 14 : Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
279 : : PyObject *result;
280 : :
281 [ - + ]: 14 : CHECK_VALID(NULL);
282 [ + + ]: 14 : if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
283 : 3 : return(NULL);
284 : :
285 : : /* silently 'adjust' out-of-range requests */
286 [ + + ]: 11 : remaining = (self->pos < self->size) ? self->size - self->pos : 0;
287 [ + + + + ]: 11 : if (num_bytes < 0 || num_bytes > remaining)
288 : 8 : num_bytes = remaining;
289 : 11 : result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
290 : 11 : self->pos += num_bytes;
291 : 11 : return result;
292 : : }
293 : :
294 : : static PyObject *
295 : 321 : mmap_gfind(mmap_object *self,
296 : : PyObject *args,
297 : : int reverse)
298 : : {
299 : 321 : Py_ssize_t start = self->pos;
300 : 321 : Py_ssize_t end = self->size;
301 : : Py_buffer view;
302 : :
303 [ - + ]: 321 : CHECK_VALID(NULL);
304 [ + + - + ]: 321 : if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
305 : : &view, &start, &end)) {
306 : 0 : return NULL;
307 : : }
308 : : else {
309 [ - + ]: 321 : if (start < 0)
310 : 0 : start += self->size;
311 [ - + ]: 321 : if (start < 0)
312 : 0 : start = 0;
313 [ - + ]: 321 : else if (start > self->size)
314 : 0 : start = self->size;
315 : :
316 [ + + ]: 321 : if (end < 0)
317 : 7 : end += self->size;
318 [ - + ]: 321 : if (end < 0)
319 : 0 : end = 0;
320 [ - + ]: 321 : else if (end > self->size)
321 : 0 : end = self->size;
322 : :
323 : : Py_ssize_t res;
324 [ + + ]: 321 : if (reverse) {
325 : 7 : res = _PyBytes_ReverseFind(
326 : 7 : self->data + start, end - start,
327 : 7 : view.buf, view.len, start);
328 : : }
329 : : else {
330 : 314 : res = _PyBytes_Find(
331 : 314 : self->data + start, end - start,
332 : 314 : view.buf, view.len, start);
333 : : }
334 : 321 : PyBuffer_Release(&view);
335 : 321 : return PyLong_FromSsize_t(res);
336 : : }
337 : : }
338 : :
339 : : static PyObject *
340 : 314 : mmap_find_method(mmap_object *self,
341 : : PyObject *args)
342 : : {
343 : 314 : return mmap_gfind(self, args, 0);
344 : : }
345 : :
346 : : static PyObject *
347 : 7 : mmap_rfind_method(mmap_object *self,
348 : : PyObject *args)
349 : : {
350 : 7 : return mmap_gfind(self, args, 1);
351 : : }
352 : :
353 : : static int
354 : 7460 : is_writable(mmap_object *self)
355 : : {
356 [ + + ]: 7460 : if (self->access != ACCESS_READ)
357 : 7455 : return 1;
358 : 5 : PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
359 : 5 : return 0;
360 : : }
361 : :
362 : : static int
363 : 5 : is_resizeable(mmap_object *self)
364 : : {
365 [ - + ]: 5 : if (self->exports > 0) {
366 : 0 : PyErr_SetString(PyExc_BufferError,
367 : : "mmap can't resize with extant buffers exported.");
368 : 0 : return 0;
369 : : }
370 [ + - + + ]: 5 : if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
371 : 3 : return 1;
372 : 2 : PyErr_Format(PyExc_TypeError,
373 : : "mmap can't resize a readonly or copy-on-write memory map.");
374 : 2 : return 0;
375 : :
376 : : }
377 : :
378 : :
379 : : static PyObject *
380 : 13 : mmap_write_method(mmap_object *self,
381 : : PyObject *args)
382 : : {
383 : : Py_buffer data;
384 : :
385 [ - + ]: 13 : CHECK_VALID(NULL);
386 [ + + ]: 13 : if (!PyArg_ParseTuple(args, "y*:write", &data))
387 : 1 : return(NULL);
388 : :
389 [ + + ]: 12 : if (!is_writable(self)) {
390 : 2 : PyBuffer_Release(&data);
391 : 2 : return NULL;
392 : : }
393 : :
394 [ + + + + ]: 10 : if (self->pos > self->size || self->size - self->pos < data.len) {
395 : 2 : PyBuffer_Release(&data);
396 : 2 : PyErr_SetString(PyExc_ValueError, "data out of range");
397 : 2 : return NULL;
398 : : }
399 : :
400 : 8 : memcpy(&self->data[self->pos], data.buf, data.len);
401 : 8 : self->pos += data.len;
402 : 8 : PyBuffer_Release(&data);
403 : 8 : return PyLong_FromSsize_t(data.len);
404 : : }
405 : :
406 : : static PyObject *
407 : 17 : mmap_write_byte_method(mmap_object *self,
408 : : PyObject *args)
409 : : {
410 : : char value;
411 : :
412 [ - + ]: 17 : CHECK_VALID(NULL);
413 [ + + ]: 17 : if (!PyArg_ParseTuple(args, "b:write_byte", &value))
414 : 1 : return(NULL);
415 : :
416 [ + + ]: 16 : if (!is_writable(self))
417 : 1 : return NULL;
418 : :
419 [ + + ]: 15 : if (self->pos < self->size) {
420 : 13 : self->data[self->pos++] = value;
421 : 13 : Py_RETURN_NONE;
422 : : }
423 : : else {
424 : 2 : PyErr_SetString(PyExc_ValueError, "write byte out of range");
425 : 2 : return NULL;
426 : : }
427 : : }
428 : :
429 : : static PyObject *
430 : 3 : mmap_size_method(mmap_object *self,
431 : : PyObject *unused)
432 : : {
433 [ - + ]: 3 : CHECK_VALID(NULL);
434 : :
435 : : #ifdef MS_WINDOWS
436 : : if (self->file_handle != INVALID_HANDLE_VALUE) {
437 : : DWORD low,high;
438 : : long long size;
439 : : low = GetFileSize(self->file_handle, &high);
440 : : if (low == INVALID_FILE_SIZE) {
441 : : /* It might be that the function appears to have failed,
442 : : when indeed its size equals INVALID_FILE_SIZE */
443 : : DWORD error = GetLastError();
444 : : if (error != NO_ERROR)
445 : : return PyErr_SetFromWindowsErr(error);
446 : : }
447 : : if (!high && low < LONG_MAX)
448 : : return PyLong_FromLong((long)low);
449 : : size = (((long long)high)<<32) + low;
450 : : return PyLong_FromLongLong(size);
451 : : } else {
452 : : return PyLong_FromSsize_t(self->size);
453 : : }
454 : : #endif /* MS_WINDOWS */
455 : :
456 : : #ifdef UNIX
457 : : {
458 : : struct _Py_stat_struct status;
459 [ - + ]: 3 : if (_Py_fstat(self->fd, &status) == -1)
460 : 0 : return NULL;
461 : : #ifdef HAVE_LARGEFILE_SUPPORT
462 : : return PyLong_FromLongLong(status.st_size);
463 : : #else
464 : 3 : return PyLong_FromLong(status.st_size);
465 : : #endif
466 : : }
467 : : #endif /* UNIX */
468 : : }
469 : :
470 : : /* This assumes that you want the entire file mapped,
471 : : / and when recreating the map will make the new file
472 : : / have the new size
473 : : /
474 : : / Is this really necessary? This could easily be done
475 : : / from python by just closing and re-opening with the
476 : : / new size?
477 : : */
478 : :
479 : : static PyObject *
480 : 5 : mmap_resize_method(mmap_object *self,
481 : : PyObject *args)
482 : : {
483 : : Py_ssize_t new_size;
484 [ - + ]: 5 : CHECK_VALID(NULL);
485 [ + - + + ]: 10 : if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
486 : 5 : !is_resizeable(self)) {
487 : 2 : return NULL;
488 : : }
489 [ + - - + ]: 3 : if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
490 : 0 : PyErr_SetString(PyExc_ValueError, "new size out of range");
491 : 0 : return NULL;
492 : : }
493 : :
494 : : {
495 : : #ifdef MS_WINDOWS
496 : : DWORD error = 0, file_resize_error = 0;
497 : : char* old_data = self->data;
498 : : LARGE_INTEGER offset, max_size;
499 : : offset.QuadPart = self->offset;
500 : : max_size.QuadPart = self->offset + new_size;
501 : : /* close the file mapping */
502 : : CloseHandle(self->map_handle);
503 : : /* if the file mapping still exists, it cannot be resized. */
504 : : if (self->tagname) {
505 : : self->map_handle = OpenFileMapping(FILE_MAP_WRITE, FALSE,
506 : : self->tagname);
507 : : if (self->map_handle) {
508 : : PyErr_SetFromWindowsErr(ERROR_USER_MAPPED_FILE);
509 : : return NULL;
510 : : }
511 : : } else {
512 : : self->map_handle = NULL;
513 : : }
514 : :
515 : : /* if it's not the paging file, unmap the view and resize the file */
516 : : if (self->file_handle != INVALID_HANDLE_VALUE) {
517 : : if (!UnmapViewOfFile(self->data)) {
518 : : return PyErr_SetFromWindowsErr(GetLastError());
519 : : };
520 : : self->data = NULL;
521 : : /* resize the file */
522 : : if (!SetFilePointerEx(self->file_handle, max_size, NULL,
523 : : FILE_BEGIN) ||
524 : : !SetEndOfFile(self->file_handle)) {
525 : : /* resizing failed. try to remap the file */
526 : : file_resize_error = GetLastError();
527 : : max_size.QuadPart = self->size;
528 : : new_size = self->size;
529 : : }
530 : : }
531 : :
532 : : /* create a new file mapping and map a new view */
533 : : /* FIXME: call CreateFileMappingW with wchar_t tagname */
534 : : self->map_handle = CreateFileMapping(
535 : : self->file_handle,
536 : : NULL,
537 : : PAGE_READWRITE,
538 : : max_size.HighPart,
539 : : max_size.LowPart,
540 : : self->tagname);
541 : :
542 : : error = GetLastError();
543 : : /* ERROR_ALREADY_EXISTS implies that between our closing the handle above and
544 : : calling CreateFileMapping here, someone's created a different mapping with
545 : : the same name. There's nothing we can usefully do so we invalidate our
546 : : mapping and error out.
547 : : */
548 : : if (error == ERROR_ALREADY_EXISTS) {
549 : : CloseHandle(self->map_handle);
550 : : self->map_handle = NULL;
551 : : }
552 : : else if (self->map_handle != NULL) {
553 : : self->data = MapViewOfFile(self->map_handle,
554 : : FILE_MAP_WRITE,
555 : : offset.HighPart,
556 : : offset.LowPart,
557 : : new_size);
558 : : if (self->data != NULL) {
559 : : /* copy the old view if using the paging file */
560 : : if (self->file_handle == INVALID_HANDLE_VALUE) {
561 : : memcpy(self->data, old_data,
562 : : self->size < new_size ? self->size : new_size);
563 : : if (!UnmapViewOfFile(old_data)) {
564 : : error = GetLastError();
565 : : }
566 : : }
567 : : self->size = new_size;
568 : : }
569 : : else {
570 : : error = GetLastError();
571 : : CloseHandle(self->map_handle);
572 : : self->map_handle = NULL;
573 : : }
574 : : }
575 : :
576 : : if (error) {
577 : : return PyErr_SetFromWindowsErr(error);
578 : : return NULL;
579 : : }
580 : : /* It's possible for a resize to fail, typically because another mapping
581 : : is still held against the same underlying file. Even if nothing has
582 : : failed -- ie we're still returning a valid file mapping -- raise the
583 : : error as an exception as the resize won't have happened
584 : : */
585 : : if (file_resize_error) {
586 : : PyErr_SetFromWindowsErr(file_resize_error);
587 : : return NULL;
588 : : }
589 : : Py_RETURN_NONE;
590 : : #endif /* MS_WINDOWS */
591 : :
592 : : #ifdef UNIX
593 : : #ifndef HAVE_MREMAP
594 : : PyErr_SetString(PyExc_SystemError,
595 : : "mmap: resizing not available--no mremap()");
596 : : return NULL;
597 : : #else
598 : : void *newmap;
599 : :
600 [ + + - + ]: 3 : if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
601 : 0 : PyErr_SetFromErrno(PyExc_OSError);
602 : 0 : return NULL;
603 : : }
604 : :
605 : : #ifdef MREMAP_MAYMOVE
606 : 3 : newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
607 : : #else
608 : : #if defined(__NetBSD__)
609 : : newmap = mremap(self->data, self->size, self->data, new_size, 0);
610 : : #else
611 : : newmap = mremap(self->data, self->size, new_size, 0);
612 : : #endif /* __NetBSD__ */
613 : : #endif
614 [ - + ]: 3 : if (newmap == (void *)-1)
615 : : {
616 : 0 : PyErr_SetFromErrno(PyExc_OSError);
617 : 0 : return NULL;
618 : : }
619 : 3 : self->data = newmap;
620 : 3 : self->size = new_size;
621 : 3 : Py_RETURN_NONE;
622 : : #endif /* HAVE_MREMAP */
623 : : #endif /* UNIX */
624 : : }
625 : : }
626 : :
627 : : static PyObject *
628 : 46 : mmap_tell_method(mmap_object *self, PyObject *unused)
629 : : {
630 [ - + ]: 46 : CHECK_VALID(NULL);
631 : 46 : return PyLong_FromSize_t(self->pos);
632 : : }
633 : :
634 : : static PyObject *
635 : 5 : mmap_flush_method(mmap_object *self, PyObject *args)
636 : : {
637 : 5 : Py_ssize_t offset = 0;
638 : 5 : Py_ssize_t size = self->size;
639 [ - + ]: 5 : CHECK_VALID(NULL);
640 [ - + ]: 5 : if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
641 : 0 : return NULL;
642 [ + - + - : 5 : if (size < 0 || offset < 0 || self->size - offset < size) {
- + ]
643 : 0 : PyErr_SetString(PyExc_ValueError, "flush values out of range");
644 : 0 : return NULL;
645 : : }
646 : :
647 [ + - + + ]: 5 : if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
648 : 1 : Py_RETURN_NONE;
649 : :
650 : : #ifdef MS_WINDOWS
651 : : if (!FlushViewOfFile(self->data+offset, size)) {
652 : : PyErr_SetFromWindowsErr(GetLastError());
653 : : return NULL;
654 : : }
655 : : Py_RETURN_NONE;
656 : : #elif defined(UNIX)
657 : : /* XXX flags for msync? */
658 [ + + ]: 4 : if (-1 == msync(self->data + offset, size, MS_SYNC)) {
659 : 1 : PyErr_SetFromErrno(PyExc_OSError);
660 : 1 : return NULL;
661 : : }
662 : 3 : Py_RETURN_NONE;
663 : : #else
664 : : PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
665 : : return NULL;
666 : : #endif
667 : : }
668 : :
669 : : static PyObject *
670 : 103 : mmap_seek_method(mmap_object *self, PyObject *args)
671 : : {
672 : : Py_ssize_t dist;
673 : 103 : int how=0;
674 [ - + ]: 103 : CHECK_VALID(NULL);
675 [ - + ]: 103 : if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
676 : 0 : return NULL;
677 : : else {
678 : : Py_ssize_t where;
679 [ + + + - ]: 103 : switch (how) {
680 : 99 : case 0: /* relative to start */
681 : 99 : where = dist;
682 : 99 : break;
683 : 1 : case 1: /* relative to current position */
684 [ - + ]: 1 : if (PY_SSIZE_T_MAX - self->pos < dist)
685 : 0 : goto onoutofrange;
686 : 1 : where = self->pos + dist;
687 : 1 : break;
688 : 3 : case 2: /* relative to end */
689 [ - + ]: 3 : if (PY_SSIZE_T_MAX - self->size < dist)
690 : 0 : goto onoutofrange;
691 : 3 : where = self->size + dist;
692 : 3 : break;
693 : 0 : default:
694 : 0 : PyErr_SetString(PyExc_ValueError, "unknown seek type");
695 : 0 : return NULL;
696 : : }
697 [ + + + + ]: 103 : if (where > self->size || where < 0)
698 : 5 : goto onoutofrange;
699 : 98 : self->pos = where;
700 : 98 : Py_RETURN_NONE;
701 : : }
702 : :
703 : 5 : onoutofrange:
704 : 5 : PyErr_SetString(PyExc_ValueError, "seek out of range");
705 : 5 : return NULL;
706 : : }
707 : :
708 : : static PyObject *
709 : 523 : mmap_move_method(mmap_object *self, PyObject *args)
710 : : {
711 : : Py_ssize_t dest, src, cnt;
712 [ - + ]: 523 : CHECK_VALID(NULL);
713 [ + - - + ]: 1046 : if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
714 : 523 : !is_writable(self)) {
715 : 0 : return NULL;
716 : : } else {
717 : : /* bounds check the values */
718 [ + + + + : 523 : if (dest < 0 || src < 0 || cnt < 0)
+ + ]
719 : 105 : goto bounds;
720 [ + + + + ]: 418 : if (self->size - dest < cnt || self->size - src < cnt)
721 : 16 : goto bounds;
722 : :
723 : 402 : memmove(&self->data[dest], &self->data[src], cnt);
724 : :
725 : 402 : Py_RETURN_NONE;
726 : :
727 : 121 : bounds:
728 : 121 : PyErr_SetString(PyExc_ValueError,
729 : : "source, destination, or count out of range");
730 : 121 : return NULL;
731 : : }
732 : : }
733 : :
734 : : static PyObject *
735 : 3 : mmap_closed_get(mmap_object *self, void *Py_UNUSED(ignored))
736 : : {
737 : : #ifdef MS_WINDOWS
738 : : return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
739 : : #elif defined(UNIX)
740 : 3 : return PyBool_FromLong(self->data == NULL ? 1 : 0);
741 : : #endif
742 : : }
743 : :
744 : : static PyObject *
745 : 88 : mmap__enter__method(mmap_object *self, PyObject *args)
746 : : {
747 [ - + ]: 88 : CHECK_VALID(NULL);
748 : :
749 : 88 : Py_INCREF(self);
750 : 88 : return (PyObject *)self;
751 : : }
752 : :
753 : : static PyObject *
754 : 88 : mmap__exit__method(PyObject *self, PyObject *args)
755 : : {
756 : 88 : return mmap_close_method((mmap_object *)self, NULL);
757 : : }
758 : :
759 : : static PyObject *
760 : 160 : mmap__repr__method(PyObject *self)
761 : : {
762 : 160 : mmap_object *mobj = (mmap_object *)self;
763 : :
764 : : #ifdef MS_WINDOWS
765 : : #define _Py_FORMAT_OFFSET "lld"
766 : : if (mobj->map_handle == NULL)
767 : : #elif defined(UNIX)
768 : : # ifdef HAVE_LARGEFILE_SUPPORT
769 : : # define _Py_FORMAT_OFFSET "lld"
770 : : # else
771 : : # define _Py_FORMAT_OFFSET "ld"
772 : : # endif
773 [ + + ]: 160 : if (mobj->data == NULL)
774 : : #endif
775 : : {
776 : 80 : return PyUnicode_FromFormat("<%s closed=True>", Py_TYPE(self)->tp_name);
777 : : } else {
778 : : const char *access_str;
779 : :
780 [ + + + + : 80 : switch (mobj->access) {
- ]
781 : 20 : case ACCESS_DEFAULT:
782 : 20 : access_str = "ACCESS_DEFAULT";
783 : 20 : break;
784 : 20 : case ACCESS_READ:
785 : 20 : access_str = "ACCESS_READ";
786 : 20 : break;
787 : 20 : case ACCESS_WRITE:
788 : 20 : access_str = "ACCESS_WRITE";
789 : 20 : break;
790 : 20 : case ACCESS_COPY:
791 : 20 : access_str = "ACCESS_COPY";
792 : 20 : break;
793 : 0 : default:
794 : 0 : Py_UNREACHABLE();
795 : : }
796 : :
797 : 80 : return PyUnicode_FromFormat("<%s closed=False, access=%s, length=%zd, "
798 : : "pos=%zd, offset=%" _Py_FORMAT_OFFSET ">",
799 : 80 : Py_TYPE(self)->tp_name, access_str,
800 : : mobj->size, mobj->pos, mobj->offset);
801 : : }
802 : : }
803 : :
804 : : #ifdef MS_WINDOWS
805 : : static PyObject *
806 : : mmap__sizeof__method(mmap_object *self, void *unused)
807 : : {
808 : : Py_ssize_t res;
809 : :
810 : : res = _PyObject_SIZE(Py_TYPE(self));
811 : : if (self->tagname)
812 : : res += strlen(self->tagname) + 1;
813 : : return PyLong_FromSsize_t(res);
814 : : }
815 : : #endif
816 : :
817 : : #ifdef HAVE_MADVISE
818 : : static PyObject *
819 : 9 : mmap_madvise_method(mmap_object *self, PyObject *args)
820 : : {
821 : : int option;
822 : 9 : Py_ssize_t start = 0, length;
823 : :
824 [ - + ]: 9 : CHECK_VALID(NULL);
825 : 9 : length = self->size;
826 : :
827 [ - + ]: 9 : if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) {
828 : 0 : return NULL;
829 : : }
830 : :
831 [ + + + + ]: 9 : if (start < 0 || start >= self->size) {
832 : 2 : PyErr_SetString(PyExc_ValueError, "madvise start out of bounds");
833 : 2 : return NULL;
834 : : }
835 [ + + ]: 7 : if (length < 0) {
836 : 1 : PyErr_SetString(PyExc_ValueError, "madvise length invalid");
837 : 1 : return NULL;
838 : : }
839 [ + + ]: 6 : if (PY_SSIZE_T_MAX - start < length) {
840 : 1 : PyErr_SetString(PyExc_OverflowError, "madvise length too large");
841 : 1 : return NULL;
842 : : }
843 : :
844 [ + + ]: 5 : if (start + length > self->size) {
845 : 2 : length = self->size - start;
846 : : }
847 : :
848 [ - + ]: 5 : if (madvise(self->data + start, length, option) != 0) {
849 : 0 : PyErr_SetFromErrno(PyExc_OSError);
850 : 0 : return NULL;
851 : : }
852 : :
853 : 5 : Py_RETURN_NONE;
854 : : }
855 : : #endif // HAVE_MADVISE
856 : :
857 : : static struct PyMemberDef mmap_object_members[] = {
858 : : {"__weaklistoffset__", T_PYSSIZET, offsetof(mmap_object, weakreflist), READONLY},
859 : : {NULL},
860 : : };
861 : :
862 : : static struct PyMethodDef mmap_object_methods[] = {
863 : : {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
864 : : {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
865 : : {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
866 : : {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
867 : : #ifdef HAVE_MADVISE
868 : : {"madvise", (PyCFunction) mmap_madvise_method, METH_VARARGS},
869 : : #endif
870 : : {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
871 : : {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
872 : : {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
873 : : {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
874 : : {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
875 : : {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
876 : : {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
877 : : {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
878 : : {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
879 : : {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
880 : : {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
881 : : {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
882 : : #ifdef MS_WINDOWS
883 : : {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
884 : : #endif
885 : : {NULL, NULL} /* sentinel */
886 : : };
887 : :
888 : : static PyGetSetDef mmap_object_getset[] = {
889 : : {"closed", (getter) mmap_closed_get, NULL, NULL},
890 : : {NULL}
891 : : };
892 : :
893 : :
894 : : /* Functions for treating an mmap'ed file as a buffer */
895 : :
896 : : static int
897 : 738 : mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
898 : : {
899 [ - + ]: 738 : CHECK_VALID(-1);
900 [ - + ]: 738 : if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
901 : 738 : (self->access == ACCESS_READ), flags) < 0)
902 : 0 : return -1;
903 : 738 : self->exports++;
904 : 738 : return 0;
905 : : }
906 : :
907 : : static void
908 : 738 : mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
909 : : {
910 : 738 : self->exports--;
911 : 738 : }
912 : :
913 : : static Py_ssize_t
914 : 8 : mmap_length(mmap_object *self)
915 : : {
916 [ - + ]: 8 : CHECK_VALID(-1);
917 : 8 : return self->size;
918 : : }
919 : :
920 : : static PyObject *
921 : 0 : mmap_item(mmap_object *self, Py_ssize_t i)
922 : : {
923 [ # # ]: 0 : CHECK_VALID(NULL);
924 [ # # # # ]: 0 : if (i < 0 || i >= self->size) {
925 : 0 : PyErr_SetString(PyExc_IndexError, "mmap index out of range");
926 : 0 : return NULL;
927 : : }
928 : 0 : return PyBytes_FromStringAndSize(self->data + i, 1);
929 : : }
930 : :
931 : : static PyObject *
932 : 12230 : mmap_subscript(mmap_object *self, PyObject *item)
933 : : {
934 [ - + ]: 12230 : CHECK_VALID(NULL);
935 [ + + ]: 12230 : if (PyIndex_Check(item)) {
936 : 8200 : Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
937 [ - + - - ]: 8200 : if (i == -1 && PyErr_Occurred())
938 : 0 : return NULL;
939 [ - + ]: 8200 : if (i < 0)
940 : 0 : i += self->size;
941 [ + - + + ]: 8200 : if (i < 0 || i >= self->size) {
942 : 2 : PyErr_SetString(PyExc_IndexError,
943 : : "mmap index out of range");
944 : 2 : return NULL;
945 : : }
946 : 8198 : return PyLong_FromLong(Py_CHARMASK(self->data[i]));
947 : : }
948 [ + - ]: 4030 : else if (PySlice_Check(item)) {
949 : : Py_ssize_t start, stop, step, slicelen;
950 : :
951 [ - + ]: 4030 : if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
952 : 0 : return NULL;
953 : : }
954 : 4030 : slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
955 : :
956 [ + + ]: 4030 : if (slicelen <= 0)
957 : 614 : return PyBytes_FromStringAndSize("", 0);
958 [ + + ]: 3416 : else if (step == 1)
959 : 2940 : return PyBytes_FromStringAndSize(self->data + start,
960 : : slicelen);
961 : : else {
962 : 476 : char *result_buf = (char *)PyMem_Malloc(slicelen);
963 : : size_t cur;
964 : : Py_ssize_t i;
965 : : PyObject *result;
966 : :
967 [ - + ]: 476 : if (result_buf == NULL)
968 : : return PyErr_NoMemory();
969 [ + + ]: 18286 : for (cur = start, i = 0; i < slicelen;
970 : 17810 : cur += step, i++) {
971 : 17810 : result_buf[i] = self->data[cur];
972 : : }
973 : 476 : result = PyBytes_FromStringAndSize(result_buf,
974 : : slicelen);
975 : 476 : PyMem_Free(result_buf);
976 : 476 : return result;
977 : : }
978 : : }
979 : : else {
980 : 0 : PyErr_SetString(PyExc_TypeError,
981 : : "mmap indices must be integers");
982 : 0 : return NULL;
983 : : }
984 : : }
985 : :
986 : : static int
987 : 0 : mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
988 : : {
989 : : const char *buf;
990 : :
991 [ # # ]: 0 : CHECK_VALID(-1);
992 [ # # # # ]: 0 : if (i < 0 || i >= self->size) {
993 : 0 : PyErr_SetString(PyExc_IndexError, "mmap index out of range");
994 : 0 : return -1;
995 : : }
996 [ # # ]: 0 : if (v == NULL) {
997 : 0 : PyErr_SetString(PyExc_TypeError,
998 : : "mmap object doesn't support item deletion");
999 : 0 : return -1;
1000 : : }
1001 [ # # # # ]: 0 : if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
1002 : 0 : PyErr_SetString(PyExc_IndexError,
1003 : : "mmap assignment must be length-1 bytes()");
1004 : 0 : return -1;
1005 : : }
1006 [ # # ]: 0 : if (!is_writable(self))
1007 : 0 : return -1;
1008 : 0 : buf = PyBytes_AsString(v);
1009 : 0 : self->data[i] = buf[0];
1010 : 0 : return 0;
1011 : : }
1012 : :
1013 : : static int
1014 : 6909 : mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
1015 : : {
1016 [ - + ]: 6909 : CHECK_VALID(-1);
1017 : :
1018 [ + + ]: 6909 : if (!is_writable(self))
1019 : 2 : return -1;
1020 : :
1021 [ + + ]: 6907 : if (PyIndex_Check(item)) {
1022 : 4098 : Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1023 : : Py_ssize_t v;
1024 : :
1025 [ - + - - ]: 4098 : if (i == -1 && PyErr_Occurred())
1026 : 0 : return -1;
1027 [ - + ]: 4098 : if (i < 0)
1028 : 0 : i += self->size;
1029 [ + - + + ]: 4098 : if (i < 0 || i >= self->size) {
1030 : 1 : PyErr_SetString(PyExc_IndexError,
1031 : : "mmap index out of range");
1032 : 1 : return -1;
1033 : : }
1034 [ - + ]: 4097 : if (value == NULL) {
1035 : 0 : PyErr_SetString(PyExc_TypeError,
1036 : : "mmap doesn't support item deletion");
1037 : 0 : return -1;
1038 : : }
1039 [ - + ]: 4097 : if (!PyIndex_Check(value)) {
1040 : 0 : PyErr_SetString(PyExc_TypeError,
1041 : : "mmap item value must be an int");
1042 : 0 : return -1;
1043 : : }
1044 : 4097 : v = PyNumber_AsSsize_t(value, PyExc_TypeError);
1045 [ - + - - ]: 4097 : if (v == -1 && PyErr_Occurred())
1046 : 0 : return -1;
1047 [ + - - + ]: 4097 : if (v < 0 || v > 255) {
1048 : 0 : PyErr_SetString(PyExc_ValueError,
1049 : : "mmap item value must be "
1050 : : "in range(0, 256)");
1051 : 0 : return -1;
1052 : : }
1053 : 4097 : self->data[i] = (char) v;
1054 : 4097 : return 0;
1055 : : }
1056 [ + - ]: 2809 : else if (PySlice_Check(item)) {
1057 : : Py_ssize_t start, stop, step, slicelen;
1058 : : Py_buffer vbuf;
1059 : :
1060 [ - + ]: 2809 : if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
1061 : 0 : return -1;
1062 : : }
1063 : 2809 : slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
1064 [ - + ]: 2809 : if (value == NULL) {
1065 : 0 : PyErr_SetString(PyExc_TypeError,
1066 : : "mmap object doesn't support slice deletion");
1067 : 0 : return -1;
1068 : : }
1069 [ - + ]: 2809 : if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
1070 : 0 : return -1;
1071 [ - + ]: 2809 : if (vbuf.len != slicelen) {
1072 : 0 : PyErr_SetString(PyExc_IndexError,
1073 : : "mmap slice assignment is wrong size");
1074 : 0 : PyBuffer_Release(&vbuf);
1075 : 0 : return -1;
1076 : : }
1077 : :
1078 [ + + ]: 2809 : if (slicelen == 0) {
1079 : : }
1080 [ + + ]: 2195 : else if (step == 1) {
1081 : 1719 : memcpy(self->data + start, vbuf.buf, slicelen);
1082 : : }
1083 : : else {
1084 : : size_t cur;
1085 : : Py_ssize_t i;
1086 : :
1087 : 476 : for (cur = start, i = 0;
1088 [ + + ]: 18286 : i < slicelen;
1089 : 17810 : cur += step, i++)
1090 : : {
1091 : 17810 : self->data[cur] = ((char *)vbuf.buf)[i];
1092 : : }
1093 : : }
1094 : 2809 : PyBuffer_Release(&vbuf);
1095 : 2809 : return 0;
1096 : : }
1097 : : else {
1098 : 0 : PyErr_SetString(PyExc_TypeError,
1099 : : "mmap indices must be integer");
1100 : 0 : return -1;
1101 : : }
1102 : : }
1103 : :
1104 : : static PyObject *
1105 : : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
1106 : :
1107 : : PyDoc_STRVAR(mmap_doc,
1108 : : "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1109 : : \n\
1110 : : Maps length bytes from the file specified by the file handle fileno,\n\
1111 : : and returns a mmap object. If length is larger than the current size\n\
1112 : : of the file, the file is extended to contain length bytes. If length\n\
1113 : : is 0, the maximum length of the map is the current size of the file,\n\
1114 : : except that if the file is empty Windows raises an exception (you cannot\n\
1115 : : create an empty mapping on Windows).\n\
1116 : : \n\
1117 : : Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1118 : : \n\
1119 : : Maps length bytes from the file specified by the file descriptor fileno,\n\
1120 : : and returns a mmap object. If length is 0, the maximum length of the map\n\
1121 : : will be the current size of the file when mmap is called.\n\
1122 : : flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1123 : : private copy-on-write mapping, so changes to the contents of the mmap\n\
1124 : : object will be private to this process, and MAP_SHARED creates a mapping\n\
1125 : : that's shared with all other processes mapping the same areas of the file.\n\
1126 : : The default value is MAP_SHARED.\n\
1127 : : \n\
1128 : : To map anonymous memory, pass -1 as the fileno (both versions).");
1129 : :
1130 : :
1131 : : static PyType_Slot mmap_object_slots[] = {
1132 : : {Py_tp_new, new_mmap_object},
1133 : : {Py_tp_dealloc, mmap_object_dealloc},
1134 : : {Py_tp_repr, mmap__repr__method},
1135 : : {Py_tp_doc, (void *)mmap_doc},
1136 : : {Py_tp_methods, mmap_object_methods},
1137 : : {Py_tp_members, mmap_object_members},
1138 : : {Py_tp_getset, mmap_object_getset},
1139 : : {Py_tp_getattro, PyObject_GenericGetAttr},
1140 : : {Py_tp_traverse, mmap_object_traverse},
1141 : :
1142 : : /* as sequence */
1143 : : {Py_sq_length, mmap_length},
1144 : : {Py_sq_item, mmap_item},
1145 : : {Py_sq_ass_item, mmap_ass_item},
1146 : :
1147 : : /* as mapping */
1148 : : {Py_mp_length, mmap_length},
1149 : : {Py_mp_subscript, mmap_subscript},
1150 : : {Py_mp_ass_subscript, mmap_ass_subscript},
1151 : :
1152 : : /* as buffer */
1153 : : {Py_bf_getbuffer, mmap_buffer_getbuf},
1154 : : {Py_bf_releasebuffer, mmap_buffer_releasebuf},
1155 : : {0, NULL},
1156 : : };
1157 : :
1158 : : static PyType_Spec mmap_object_spec = {
1159 : : .name = "mmap.mmap",
1160 : : .basicsize = sizeof(mmap_object),
1161 : : .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
1162 : : Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
1163 : : .slots = mmap_object_slots,
1164 : : };
1165 : :
1166 : :
1167 : : #ifdef UNIX
1168 : : #ifdef HAVE_LARGEFILE_SUPPORT
1169 : : #define _Py_PARSE_OFF_T "L"
1170 : : #else
1171 : : #define _Py_PARSE_OFF_T "l"
1172 : : #endif
1173 : :
1174 : : static PyObject *
1175 : 899 : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1176 : : {
1177 : : struct _Py_stat_struct status;
1178 : 899 : int fstat_result = -1;
1179 : : mmap_object *m_obj;
1180 : : Py_ssize_t map_size;
1181 : 899 : off_t offset = 0;
1182 : 899 : int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1183 : 899 : int devzero = -1;
1184 : 899 : int access = (int)ACCESS_DEFAULT;
1185 : : static char *keywords[] = {"fileno", "length",
1186 : : "flags", "prot",
1187 : : "access", "offset", NULL};
1188 : :
1189 [ + + ]: 899 : if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1190 : : &fd, &map_size, &flags, &prot,
1191 : : &access, &offset))
1192 : 1 : return NULL;
1193 [ - + ]: 898 : if (map_size < 0) {
1194 : 0 : PyErr_SetString(PyExc_OverflowError,
1195 : : "memory mapped length must be positive");
1196 : 0 : return NULL;
1197 : : }
1198 [ + + ]: 898 : if (offset < 0) {
1199 : 2 : PyErr_SetString(PyExc_OverflowError,
1200 : : "memory mapped offset must be positive");
1201 : 2 : return NULL;
1202 : : }
1203 : :
1204 [ + + ]: 896 : if ((access != (int)ACCESS_DEFAULT) &&
1205 [ + + - + ]: 72 : ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1206 : 1 : return PyErr_Format(PyExc_ValueError,
1207 : : "mmap can't specify both access and flags, prot.");
1208 [ + + + + : 895 : switch ((access_mode)access) {
+ ]
1209 : 28 : case ACCESS_READ:
1210 : 28 : flags = MAP_SHARED;
1211 : 28 : prot = PROT_READ;
1212 : 28 : break;
1213 : 21 : case ACCESS_WRITE:
1214 : 21 : flags = MAP_SHARED;
1215 : 21 : prot = PROT_READ | PROT_WRITE;
1216 : 21 : break;
1217 : 21 : case ACCESS_COPY:
1218 : 21 : flags = MAP_PRIVATE;
1219 : 21 : prot = PROT_READ | PROT_WRITE;
1220 : 21 : break;
1221 : 824 : case ACCESS_DEFAULT:
1222 : : /* map prot to access type */
1223 [ + - + + ]: 824 : if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1224 : : /* ACCESS_DEFAULT */
1225 : : }
1226 [ - + ]: 2 : else if (prot & PROT_WRITE) {
1227 : 0 : access = ACCESS_WRITE;
1228 : : }
1229 : : else {
1230 : 2 : access = ACCESS_READ;
1231 : : }
1232 : 824 : break;
1233 : 1 : default:
1234 : 1 : return PyErr_Format(PyExc_ValueError,
1235 : : "mmap invalid access parameter.");
1236 : : }
1237 : :
1238 [ - + ]: 894 : if (PySys_Audit("mmap.__new__", "ini" _Py_PARSE_OFF_T,
1239 : : fd, map_size, access, offset) < 0) {
1240 : 0 : return NULL;
1241 : : }
1242 : :
1243 : : #ifdef __APPLE__
1244 : : /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1245 : : fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1246 : : if (fd != -1)
1247 : : (void)fcntl(fd, F_FULLFSYNC);
1248 : : #endif
1249 : :
1250 [ + + ]: 894 : if (fd != -1) {
1251 : 489 : Py_BEGIN_ALLOW_THREADS
1252 : 489 : fstat_result = _Py_fstat_noraise(fd, &status);
1253 : 489 : Py_END_ALLOW_THREADS
1254 : : }
1255 : :
1256 [ + + + + : 894 : if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
+ - ]
1257 [ + + ]: 488 : if (map_size == 0) {
1258 [ + + ]: 8 : if (status.st_size == 0) {
1259 : 2 : PyErr_SetString(PyExc_ValueError,
1260 : : "cannot mmap an empty file");
1261 : 2 : return NULL;
1262 : : }
1263 [ - + ]: 6 : if (offset >= status.st_size) {
1264 : 0 : PyErr_SetString(PyExc_ValueError,
1265 : : "mmap offset is greater than file size");
1266 : 0 : return NULL;
1267 : : }
1268 : : if (status.st_size - offset > PY_SSIZE_T_MAX) {
1269 : : PyErr_SetString(PyExc_ValueError,
1270 : : "mmap length is too large");
1271 : : return NULL;
1272 : : }
1273 : 6 : map_size = (Py_ssize_t) (status.st_size - offset);
1274 [ + - + + ]: 480 : } else if (offset > status.st_size || status.st_size - offset < map_size) {
1275 : 1 : PyErr_SetString(PyExc_ValueError,
1276 : : "mmap length is greater than file size");
1277 : 1 : return NULL;
1278 : : }
1279 : : }
1280 : 891 : m_obj = (mmap_object *)type->tp_alloc(type, 0);
1281 [ - + ]: 891 : if (m_obj == NULL) {return NULL;}
1282 : 891 : m_obj->data = NULL;
1283 : 891 : m_obj->size = map_size;
1284 : 891 : m_obj->pos = 0;
1285 : 891 : m_obj->weakreflist = NULL;
1286 : 891 : m_obj->exports = 0;
1287 : 891 : m_obj->offset = offset;
1288 [ + + ]: 891 : if (fd == -1) {
1289 : 405 : m_obj->fd = -1;
1290 : : /* Assume the caller wants to map anonymous memory.
1291 : : This is the same behaviour as Windows. mmap.mmap(-1, size)
1292 : : on both Windows and Unix map anonymous memory.
1293 : : */
1294 : : #ifdef MAP_ANONYMOUS
1295 : : /* BSD way to map anonymous memory */
1296 : 405 : flags |= MAP_ANONYMOUS;
1297 : :
1298 : : /* VxWorks only supports MAP_ANONYMOUS with MAP_PRIVATE flag */
1299 : : #ifdef __VXWORKS__
1300 : : flags &= ~MAP_SHARED;
1301 : : flags |= MAP_PRIVATE;
1302 : : #endif
1303 : :
1304 : : #else
1305 : : /* SVR4 method to map anonymous memory is to open /dev/zero */
1306 : : fd = devzero = _Py_open("/dev/zero", O_RDWR);
1307 : : if (devzero == -1) {
1308 : : Py_DECREF(m_obj);
1309 : : return NULL;
1310 : : }
1311 : : #endif
1312 : : }
1313 : : else {
1314 : 486 : m_obj->fd = _Py_dup(fd);
1315 [ + + ]: 486 : if (m_obj->fd == -1) {
1316 : 1 : Py_DECREF(m_obj);
1317 : 1 : return NULL;
1318 : : }
1319 : : }
1320 : :
1321 : 890 : m_obj->data = mmap(NULL, map_size,
1322 : : prot, flags,
1323 : : fd, offset);
1324 : :
1325 [ - + ]: 890 : if (devzero != -1) {
1326 : 0 : close(devzero);
1327 : : }
1328 : :
1329 [ - + ]: 890 : if (m_obj->data == (char *)-1) {
1330 : 0 : m_obj->data = NULL;
1331 : 0 : Py_DECREF(m_obj);
1332 : 0 : PyErr_SetFromErrno(PyExc_OSError);
1333 : 0 : return NULL;
1334 : : }
1335 : 890 : m_obj->access = (access_mode)access;
1336 : 890 : return (PyObject *)m_obj;
1337 : : }
1338 : : #endif /* UNIX */
1339 : :
1340 : : #ifdef MS_WINDOWS
1341 : :
1342 : : /* A note on sizes and offsets: while the actual map size must hold in a
1343 : : Py_ssize_t, both the total file size and the start offset can be longer
1344 : : than a Py_ssize_t, so we use long long which is always 64-bit.
1345 : : */
1346 : :
1347 : : static PyObject *
1348 : : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1349 : : {
1350 : : mmap_object *m_obj;
1351 : : Py_ssize_t map_size;
1352 : : long long offset = 0, size;
1353 : : DWORD off_hi; /* upper 32 bits of offset */
1354 : : DWORD off_lo; /* lower 32 bits of offset */
1355 : : DWORD size_hi; /* upper 32 bits of size */
1356 : : DWORD size_lo; /* lower 32 bits of size */
1357 : : const char *tagname = "";
1358 : : DWORD dwErr = 0;
1359 : : int fileno;
1360 : : HANDLE fh = 0;
1361 : : int access = (access_mode)ACCESS_DEFAULT;
1362 : : DWORD flProtect, dwDesiredAccess;
1363 : : static char *keywords[] = { "fileno", "length",
1364 : : "tagname",
1365 : : "access", "offset", NULL };
1366 : :
1367 : : if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1368 : : &fileno, &map_size,
1369 : : &tagname, &access, &offset)) {
1370 : : return NULL;
1371 : : }
1372 : :
1373 : : if (PySys_Audit("mmap.__new__", "iniL",
1374 : : fileno, map_size, access, offset) < 0) {
1375 : : return NULL;
1376 : : }
1377 : :
1378 : : switch((access_mode)access) {
1379 : : case ACCESS_READ:
1380 : : flProtect = PAGE_READONLY;
1381 : : dwDesiredAccess = FILE_MAP_READ;
1382 : : break;
1383 : : case ACCESS_DEFAULT: case ACCESS_WRITE:
1384 : : flProtect = PAGE_READWRITE;
1385 : : dwDesiredAccess = FILE_MAP_WRITE;
1386 : : break;
1387 : : case ACCESS_COPY:
1388 : : flProtect = PAGE_WRITECOPY;
1389 : : dwDesiredAccess = FILE_MAP_COPY;
1390 : : break;
1391 : : default:
1392 : : return PyErr_Format(PyExc_ValueError,
1393 : : "mmap invalid access parameter.");
1394 : : }
1395 : :
1396 : : if (map_size < 0) {
1397 : : PyErr_SetString(PyExc_OverflowError,
1398 : : "memory mapped length must be positive");
1399 : : return NULL;
1400 : : }
1401 : : if (offset < 0) {
1402 : : PyErr_SetString(PyExc_OverflowError,
1403 : : "memory mapped offset must be positive");
1404 : : return NULL;
1405 : : }
1406 : :
1407 : : /* assume -1 and 0 both mean invalid filedescriptor
1408 : : to 'anonymously' map memory.
1409 : : XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1410 : : XXX: Should this code be added?
1411 : : if (fileno == 0)
1412 : : PyErr_WarnEx(PyExc_DeprecationWarning,
1413 : : "don't use 0 for anonymous memory",
1414 : : 1);
1415 : : */
1416 : : if (fileno != -1 && fileno != 0) {
1417 : : /* Ensure that fileno is within the CRT's valid range */
1418 : : fh = _Py_get_osfhandle(fileno);
1419 : : if (fh == INVALID_HANDLE_VALUE)
1420 : : return NULL;
1421 : :
1422 : : /* Win9x appears to need us seeked to zero */
1423 : : lseek(fileno, 0, SEEK_SET);
1424 : : }
1425 : :
1426 : : m_obj = (mmap_object *)type->tp_alloc(type, 0);
1427 : : if (m_obj == NULL)
1428 : : return NULL;
1429 : : /* Set every field to an invalid marker, so we can safely
1430 : : destruct the object in the face of failure */
1431 : : m_obj->data = NULL;
1432 : : m_obj->file_handle = INVALID_HANDLE_VALUE;
1433 : : m_obj->map_handle = NULL;
1434 : : m_obj->tagname = NULL;
1435 : : m_obj->offset = offset;
1436 : :
1437 : : if (fh) {
1438 : : /* It is necessary to duplicate the handle, so the
1439 : : Python code can close it on us */
1440 : : if (!DuplicateHandle(
1441 : : GetCurrentProcess(), /* source process handle */
1442 : : fh, /* handle to be duplicated */
1443 : : GetCurrentProcess(), /* target proc handle */
1444 : : (LPHANDLE)&m_obj->file_handle, /* result */
1445 : : 0, /* access - ignored due to options value */
1446 : : FALSE, /* inherited by child processes? */
1447 : : DUPLICATE_SAME_ACCESS)) { /* options */
1448 : : dwErr = GetLastError();
1449 : : Py_DECREF(m_obj);
1450 : : PyErr_SetFromWindowsErr(dwErr);
1451 : : return NULL;
1452 : : }
1453 : : if (!map_size) {
1454 : : DWORD low,high;
1455 : : low = GetFileSize(fh, &high);
1456 : : /* low might just happen to have the value INVALID_FILE_SIZE;
1457 : : so we need to check the last error also. */
1458 : : if (low == INVALID_FILE_SIZE &&
1459 : : (dwErr = GetLastError()) != NO_ERROR) {
1460 : : Py_DECREF(m_obj);
1461 : : return PyErr_SetFromWindowsErr(dwErr);
1462 : : }
1463 : :
1464 : : size = (((long long) high) << 32) + low;
1465 : : if (size == 0) {
1466 : : PyErr_SetString(PyExc_ValueError,
1467 : : "cannot mmap an empty file");
1468 : : Py_DECREF(m_obj);
1469 : : return NULL;
1470 : : }
1471 : : if (offset >= size) {
1472 : : PyErr_SetString(PyExc_ValueError,
1473 : : "mmap offset is greater than file size");
1474 : : Py_DECREF(m_obj);
1475 : : return NULL;
1476 : : }
1477 : : if (size - offset > PY_SSIZE_T_MAX) {
1478 : : PyErr_SetString(PyExc_ValueError,
1479 : : "mmap length is too large");
1480 : : Py_DECREF(m_obj);
1481 : : return NULL;
1482 : : }
1483 : : m_obj->size = (Py_ssize_t) (size - offset);
1484 : : } else {
1485 : : m_obj->size = map_size;
1486 : : size = offset + map_size;
1487 : : }
1488 : : }
1489 : : else {
1490 : : m_obj->size = map_size;
1491 : : size = offset + map_size;
1492 : : }
1493 : :
1494 : : /* set the initial position */
1495 : : m_obj->pos = (size_t) 0;
1496 : :
1497 : : m_obj->weakreflist = NULL;
1498 : : m_obj->exports = 0;
1499 : : /* set the tag name */
1500 : : if (tagname != NULL && *tagname != '\0') {
1501 : : m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1502 : : if (m_obj->tagname == NULL) {
1503 : : PyErr_NoMemory();
1504 : : Py_DECREF(m_obj);
1505 : : return NULL;
1506 : : }
1507 : : strcpy(m_obj->tagname, tagname);
1508 : : }
1509 : : else
1510 : : m_obj->tagname = NULL;
1511 : :
1512 : : m_obj->access = (access_mode)access;
1513 : : size_hi = (DWORD)(size >> 32);
1514 : : size_lo = (DWORD)(size & 0xFFFFFFFF);
1515 : : off_hi = (DWORD)(offset >> 32);
1516 : : off_lo = (DWORD)(offset & 0xFFFFFFFF);
1517 : : /* For files, it would be sufficient to pass 0 as size.
1518 : : For anonymous maps, we have to pass the size explicitly. */
1519 : : m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1520 : : NULL,
1521 : : flProtect,
1522 : : size_hi,
1523 : : size_lo,
1524 : : m_obj->tagname);
1525 : : if (m_obj->map_handle != NULL) {
1526 : : m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1527 : : dwDesiredAccess,
1528 : : off_hi,
1529 : : off_lo,
1530 : : m_obj->size);
1531 : : if (m_obj->data != NULL)
1532 : : return (PyObject *)m_obj;
1533 : : else {
1534 : : dwErr = GetLastError();
1535 : : CloseHandle(m_obj->map_handle);
1536 : : m_obj->map_handle = NULL;
1537 : : }
1538 : : } else
1539 : : dwErr = GetLastError();
1540 : : Py_DECREF(m_obj);
1541 : : PyErr_SetFromWindowsErr(dwErr);
1542 : : return NULL;
1543 : : }
1544 : : #endif /* MS_WINDOWS */
1545 : :
1546 : : static int
1547 : 341 : mmap_exec(PyObject *module)
1548 : : {
1549 : 341 : Py_INCREF(PyExc_OSError);
1550 [ - + ]: 341 : if (PyModule_AddObject(module, "error", PyExc_OSError) < 0) {
1551 : 0 : Py_DECREF(PyExc_OSError);
1552 : 0 : return -1;
1553 : : }
1554 : :
1555 : 341 : PyObject *mmap_object_type = PyType_FromModuleAndSpec(module,
1556 : : &mmap_object_spec, NULL);
1557 [ - + ]: 341 : if (mmap_object_type == NULL) {
1558 : 0 : return -1;
1559 : : }
1560 : 341 : int rc = PyModule_AddType(module, (PyTypeObject *)mmap_object_type);
1561 : 341 : Py_DECREF(mmap_object_type);
1562 [ - + ]: 341 : if (rc < 0) {
1563 : 0 : return -1;
1564 : : }
1565 : :
1566 : : #define ADD_INT_MACRO(module, constant) \
1567 : : do { \
1568 : : if (PyModule_AddIntConstant(module, #constant, constant) < 0) { \
1569 : : return -1; \
1570 : : } \
1571 : : } while (0)
1572 : :
1573 : : #ifdef PROT_EXEC
1574 [ - + ]: 341 : ADD_INT_MACRO(module, PROT_EXEC);
1575 : : #endif
1576 : : #ifdef PROT_READ
1577 [ - + ]: 341 : ADD_INT_MACRO(module, PROT_READ);
1578 : : #endif
1579 : : #ifdef PROT_WRITE
1580 [ - + ]: 341 : ADD_INT_MACRO(module, PROT_WRITE);
1581 : : #endif
1582 : :
1583 : : #ifdef MAP_SHARED
1584 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_SHARED);
1585 : : #endif
1586 : : #ifdef MAP_PRIVATE
1587 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_PRIVATE);
1588 : : #endif
1589 : : #ifdef MAP_DENYWRITE
1590 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_DENYWRITE);
1591 : : #endif
1592 : : #ifdef MAP_EXECUTABLE
1593 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_EXECUTABLE);
1594 : : #endif
1595 : : #ifdef MAP_ANONYMOUS
1596 [ - + ]: 341 : if (PyModule_AddIntConstant(module, "MAP_ANON", MAP_ANONYMOUS) < 0 ) {
1597 : 0 : return -1;
1598 : : }
1599 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_ANONYMOUS);
1600 : : #endif
1601 : : #ifdef MAP_POPULATE
1602 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_POPULATE);
1603 : : #endif
1604 : : #ifdef MAP_STACK
1605 : : // Mostly a no-op on Linux and NetBSD, but useful on OpenBSD
1606 : : // for stack usage (even on x86 arch)
1607 [ - + ]: 341 : ADD_INT_MACRO(module, MAP_STACK);
1608 : : #endif
1609 [ - + ]: 341 : if (PyModule_AddIntConstant(module, "PAGESIZE", (long)my_getpagesize()) < 0 ) {
1610 : 0 : return -1;
1611 : : }
1612 : :
1613 [ - + ]: 341 : if (PyModule_AddIntConstant(module, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity()) < 0 ) {
1614 : 0 : return -1;
1615 : : }
1616 : :
1617 [ - + ]: 341 : ADD_INT_MACRO(module, ACCESS_DEFAULT);
1618 [ - + ]: 341 : ADD_INT_MACRO(module, ACCESS_READ);
1619 [ - + ]: 341 : ADD_INT_MACRO(module, ACCESS_WRITE);
1620 [ - + ]: 341 : ADD_INT_MACRO(module, ACCESS_COPY);
1621 : :
1622 : : #ifdef HAVE_MADVISE
1623 : : // Conventional advice values
1624 : : #ifdef MADV_NORMAL
1625 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_NORMAL);
1626 : : #endif
1627 : : #ifdef MADV_RANDOM
1628 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_RANDOM);
1629 : : #endif
1630 : : #ifdef MADV_SEQUENTIAL
1631 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_SEQUENTIAL);
1632 : : #endif
1633 : : #ifdef MADV_WILLNEED
1634 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_WILLNEED);
1635 : : #endif
1636 : : #ifdef MADV_DONTNEED
1637 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_DONTNEED);
1638 : : #endif
1639 : :
1640 : : // Linux-specific advice values
1641 : : #ifdef MADV_REMOVE
1642 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_REMOVE);
1643 : : #endif
1644 : : #ifdef MADV_DONTFORK
1645 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_DONTFORK);
1646 : : #endif
1647 : : #ifdef MADV_DOFORK
1648 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_DOFORK);
1649 : : #endif
1650 : : #ifdef MADV_HWPOISON
1651 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_HWPOISON);
1652 : : #endif
1653 : : #ifdef MADV_MERGEABLE
1654 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_MERGEABLE);
1655 : : #endif
1656 : : #ifdef MADV_UNMERGEABLE
1657 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_UNMERGEABLE);
1658 : : #endif
1659 : : #ifdef MADV_SOFT_OFFLINE
1660 : : ADD_INT_MACRO(module, MADV_SOFT_OFFLINE);
1661 : : #endif
1662 : : #ifdef MADV_HUGEPAGE
1663 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_HUGEPAGE);
1664 : : #endif
1665 : : #ifdef MADV_NOHUGEPAGE
1666 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_NOHUGEPAGE);
1667 : : #endif
1668 : : #ifdef MADV_DONTDUMP
1669 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_DONTDUMP);
1670 : : #endif
1671 : : #ifdef MADV_DODUMP
1672 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_DODUMP);
1673 : : #endif
1674 : : #ifdef MADV_FREE // (Also present on FreeBSD and macOS.)
1675 [ - + ]: 341 : ADD_INT_MACRO(module, MADV_FREE);
1676 : : #endif
1677 : :
1678 : : // FreeBSD-specific
1679 : : #ifdef MADV_NOSYNC
1680 : : ADD_INT_MACRO(module, MADV_NOSYNC);
1681 : : #endif
1682 : : #ifdef MADV_AUTOSYNC
1683 : : ADD_INT_MACRO(module, MADV_AUTOSYNC);
1684 : : #endif
1685 : : #ifdef MADV_NOCORE
1686 : : ADD_INT_MACRO(module, MADV_NOCORE);
1687 : : #endif
1688 : : #ifdef MADV_CORE
1689 : : ADD_INT_MACRO(module, MADV_CORE);
1690 : : #endif
1691 : : #ifdef MADV_PROTECT
1692 : : ADD_INT_MACRO(module, MADV_PROTECT);
1693 : : #endif
1694 : :
1695 : : // Darwin-specific
1696 : : #ifdef MADV_FREE_REUSABLE // (As MADV_FREE but reclaims more faithful for task_info/Activity Monitor...)
1697 : : ADD_INT_MACRO(module, MADV_FREE_REUSABLE);
1698 : : #endif
1699 : : #ifdef MADV_FREE_REUSE // (Reuse pages previously tagged as reusable)
1700 : : ADD_INT_MACRO(module, MADV_FREE_REUSE);
1701 : : #endif
1702 : : #endif // HAVE_MADVISE
1703 : 341 : return 0;
1704 : : }
1705 : :
1706 : : static PyModuleDef_Slot mmap_slots[] = {
1707 : : {Py_mod_exec, mmap_exec},
1708 : : {0, NULL}
1709 : : };
1710 : :
1711 : : static struct PyModuleDef mmapmodule = {
1712 : : .m_base = PyModuleDef_HEAD_INIT,
1713 : : .m_name = "mmap",
1714 : : .m_size = 0,
1715 : : .m_slots = mmap_slots,
1716 : : };
1717 : :
1718 : : PyMODINIT_FUNC
1719 : 341 : PyInit_mmap(void)
1720 : : {
1721 : 341 : return PyModuleDef_Init(&mmapmodule);
1722 : : }
|