LCOV - code coverage report
Current view: top level - Modules - mmapmodule.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 406 516 78.7 %
Date: 2022-07-07 18:19:46 Functions: 32 35 91.4 %

          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         676 : my_getpagesize(void)
      67             : {
      68         676 :     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        1184 : mmap_object_traverse(mmap_object *m_obj, visitproc visit, void *arg)
     125             : {
     126        1184 :     Py_VISIT(Py_TYPE(m_obj));
     127        1184 :     return 0;
     128             : }
     129             : 
     130             : static void
     131         907 : mmap_object_dealloc(mmap_object *m_obj)
     132             : {
     133         907 :     PyTypeObject *tp = Py_TYPE(m_obj);
     134         907 :     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         907 :     Py_BEGIN_ALLOW_THREADS
     151         907 :     if (m_obj->fd >= 0)
     152         141 :         (void) close(m_obj->fd);
     153         907 :     if (m_obj->data!=NULL) {
     154         151 :         munmap(m_obj->data, m_obj->size);
     155             :     }
     156         907 :     Py_END_ALLOW_THREADS
     157             : #endif /* UNIX */
     158             : 
     159         907 :     if (m_obj->weakreflist != NULL)
     160           1 :         PyObject_ClearWeakRefs((PyObject *) m_obj);
     161             : 
     162         907 :     tp->tp_free(m_obj);
     163         907 :     Py_DECREF(tp);
     164         907 : }
     165             : 
     166             : static PyObject *
     167         756 : mmap_close_method(mmap_object *self, PyObject *unused)
     168             : {
     169         756 :     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         756 :     int fd = self->fd;
     203         756 :     char *data = self->data;
     204         756 :     self->fd = -1;
     205         756 :     self->data = NULL;
     206         756 :     Py_BEGIN_ALLOW_THREADS
     207         756 :     if (0 <= fd)
     208         360 :         (void) close(fd);
     209         756 :     if (data != NULL) {
     210         755 :         munmap(data, self->size);
     211             :     }
     212         756 :     Py_END_ALLOW_THREADS
     213             : #endif
     214             : 
     215         756 :     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         743 : mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
     898             : {
     899         743 :     CHECK_VALID(-1);
     900         743 :     if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
     901         743 :                           (self->access == ACCESS_READ), flags) < 0)
     902           0 :         return -1;
     903         743 :     self->exports++;
     904         743 :     return 0;
     905             : }
     906             : 
     907             : static void
     908         743 : mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
     909             : {
     910         743 :     self->exports--;
     911         743 : }
     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           0 :                 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       18286 :             for (cur = start, i = 0;
    1088             :                  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         915 : new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
    1176             : {
    1177             :     struct _Py_stat_struct status;
    1178         915 :     int fstat_result = -1;
    1179             :     mmap_object *m_obj;
    1180             :     Py_ssize_t map_size;
    1181         915 :     off_t offset = 0;
    1182         915 :     int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
    1183         915 :     int devzero = -1;
    1184         915 :     int access = (int)ACCESS_DEFAULT;
    1185             :     static char *keywords[] = {"fileno", "length",
    1186             :                                "flags", "prot",
    1187             :                                "access", "offset", NULL};
    1188             : 
    1189         915 :     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         914 :     if (map_size < 0) {
    1194           0 :         PyErr_SetString(PyExc_OverflowError,
    1195             :                         "memory mapped length must be positive");
    1196           0 :         return NULL;
    1197             :     }
    1198         914 :     if (offset < 0) {
    1199           2 :         PyErr_SetString(PyExc_OverflowError,
    1200             :             "memory mapped offset must be positive");
    1201           2 :         return NULL;
    1202             :     }
    1203             : 
    1204         912 :     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         911 :     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         840 :     case ACCESS_DEFAULT:
    1222             :         /* map prot to access type */
    1223         840 :         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         840 :         break;
    1233           1 :     default:
    1234           1 :         return PyErr_Format(PyExc_ValueError,
    1235             :                             "mmap invalid access parameter.");
    1236             :     }
    1237             : 
    1238         910 :     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         910 :     if (fd != -1) {
    1251         505 :         Py_BEGIN_ALLOW_THREADS
    1252         505 :         fstat_result = _Py_fstat_noraise(fd, &status);
    1253         505 :         Py_END_ALLOW_THREADS
    1254             :     }
    1255             : 
    1256         910 :     if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
    1257         504 :         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         496 :         } 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         907 :     m_obj = (mmap_object *)type->tp_alloc(type, 0);
    1281         907 :     if (m_obj == NULL) {return NULL;}
    1282         907 :     m_obj->data = NULL;
    1283         907 :     m_obj->size = map_size;
    1284         907 :     m_obj->pos = 0;
    1285         907 :     m_obj->weakreflist = NULL;
    1286         907 :     m_obj->exports = 0;
    1287         907 :     m_obj->offset = offset;
    1288         907 :     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         502 :         m_obj->fd = _Py_dup(fd);
    1315         502 :         if (m_obj->fd == -1) {
    1316           1 :             Py_DECREF(m_obj);
    1317           1 :             return NULL;
    1318             :         }
    1319             :     }
    1320             : 
    1321         906 :     m_obj->data = mmap(NULL, map_size,
    1322             :                        prot, flags,
    1323             :                        fd, offset);
    1324             : 
    1325         906 :     if (devzero != -1) {
    1326           0 :         close(devzero);
    1327             :     }
    1328             : 
    1329         906 :     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         906 :     m_obj->access = (access_mode)access;
    1336         906 :     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         338 : mmap_exec(PyObject *module)
    1548             : {
    1549         338 :     Py_INCREF(PyExc_OSError);
    1550         338 :     if (PyModule_AddObject(module, "error", PyExc_OSError) < 0) {
    1551           0 :         Py_DECREF(PyExc_OSError);
    1552           0 :         return -1;
    1553             :     }
    1554             : 
    1555         338 :     PyObject *mmap_object_type = PyType_FromModuleAndSpec(module,
    1556             :                                                   &mmap_object_spec, NULL);
    1557         338 :     if (mmap_object_type == NULL) {
    1558           0 :         return -1;
    1559             :     }
    1560         338 :     int rc = PyModule_AddType(module, (PyTypeObject *)mmap_object_type);
    1561         338 :     Py_DECREF(mmap_object_type);
    1562         338 :     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         338 :     ADD_INT_MACRO(module, PROT_EXEC);
    1575             : #endif
    1576             : #ifdef PROT_READ
    1577         338 :     ADD_INT_MACRO(module, PROT_READ);
    1578             : #endif
    1579             : #ifdef PROT_WRITE
    1580         338 :     ADD_INT_MACRO(module, PROT_WRITE);
    1581             : #endif
    1582             : 
    1583             : #ifdef MAP_SHARED
    1584         338 :     ADD_INT_MACRO(module, MAP_SHARED);
    1585             : #endif
    1586             : #ifdef MAP_PRIVATE
    1587         338 :     ADD_INT_MACRO(module, MAP_PRIVATE);
    1588             : #endif
    1589             : #ifdef MAP_DENYWRITE
    1590         338 :     ADD_INT_MACRO(module, MAP_DENYWRITE);
    1591             : #endif
    1592             : #ifdef MAP_EXECUTABLE
    1593         338 :     ADD_INT_MACRO(module, MAP_EXECUTABLE);
    1594             : #endif
    1595             : #ifdef MAP_ANONYMOUS
    1596         338 :     if (PyModule_AddIntConstant(module, "MAP_ANON", MAP_ANONYMOUS) < 0 ) {
    1597           0 :         return -1;
    1598             :     }
    1599         338 :     ADD_INT_MACRO(module, MAP_ANONYMOUS);
    1600             : #endif
    1601             : #ifdef MAP_POPULATE
    1602         338 :     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         338 :     ADD_INT_MACRO(module, MAP_STACK);
    1608             : #endif
    1609         338 :     if (PyModule_AddIntConstant(module, "PAGESIZE", (long)my_getpagesize()) < 0 ) {
    1610           0 :         return -1;
    1611             :     }
    1612             : 
    1613         338 :     if (PyModule_AddIntConstant(module, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity()) < 0 ) {
    1614           0 :         return -1;
    1615             :     }
    1616             : 
    1617         338 :     ADD_INT_MACRO(module, ACCESS_DEFAULT);
    1618         338 :     ADD_INT_MACRO(module, ACCESS_READ);
    1619         338 :     ADD_INT_MACRO(module, ACCESS_WRITE);
    1620         338 :     ADD_INT_MACRO(module, ACCESS_COPY);
    1621             : 
    1622             : #ifdef HAVE_MADVISE
    1623             :     // Conventional advice values
    1624             : #ifdef MADV_NORMAL
    1625         338 :     ADD_INT_MACRO(module, MADV_NORMAL);
    1626             : #endif
    1627             : #ifdef MADV_RANDOM
    1628         338 :     ADD_INT_MACRO(module, MADV_RANDOM);
    1629             : #endif
    1630             : #ifdef MADV_SEQUENTIAL
    1631         338 :     ADD_INT_MACRO(module, MADV_SEQUENTIAL);
    1632             : #endif
    1633             : #ifdef MADV_WILLNEED
    1634         338 :     ADD_INT_MACRO(module, MADV_WILLNEED);
    1635             : #endif
    1636             : #ifdef MADV_DONTNEED
    1637         338 :     ADD_INT_MACRO(module, MADV_DONTNEED);
    1638             : #endif
    1639             : 
    1640             :     // Linux-specific advice values
    1641             : #ifdef MADV_REMOVE
    1642         338 :     ADD_INT_MACRO(module, MADV_REMOVE);
    1643             : #endif
    1644             : #ifdef MADV_DONTFORK
    1645         338 :     ADD_INT_MACRO(module, MADV_DONTFORK);
    1646             : #endif
    1647             : #ifdef MADV_DOFORK
    1648         338 :     ADD_INT_MACRO(module, MADV_DOFORK);
    1649             : #endif
    1650             : #ifdef MADV_HWPOISON
    1651         338 :     ADD_INT_MACRO(module, MADV_HWPOISON);
    1652             : #endif
    1653             : #ifdef MADV_MERGEABLE
    1654         338 :     ADD_INT_MACRO(module, MADV_MERGEABLE);
    1655             : #endif
    1656             : #ifdef MADV_UNMERGEABLE
    1657         338 :     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         338 :     ADD_INT_MACRO(module, MADV_HUGEPAGE);
    1664             : #endif
    1665             : #ifdef MADV_NOHUGEPAGE
    1666         338 :     ADD_INT_MACRO(module, MADV_NOHUGEPAGE);
    1667             : #endif
    1668             : #ifdef MADV_DONTDUMP
    1669         338 :     ADD_INT_MACRO(module, MADV_DONTDUMP);
    1670             : #endif
    1671             : #ifdef MADV_DODUMP
    1672         338 :     ADD_INT_MACRO(module, MADV_DODUMP);
    1673             : #endif
    1674             : #ifdef MADV_FREE // (Also present on FreeBSD and macOS.)
    1675         338 :     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         338 :     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         338 : PyInit_mmap(void)
    1720             : {
    1721         338 :     return PyModuleDef_Init(&mmapmodule);
    1722             : }

Generated by: LCOV version 1.14