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