LCOV - code coverage report
Current view: top level - Modules - mmapmodule.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit acb105a7c1f] Lines: 407 516 78.9 %
Date: 2022-07-20 13:12:14 Functions: 32 35 91.4 %
Branches: 251 400 62.8 %

           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                 :            : }

Generated by: LCOV version 1.14