Home | History | Annotate | Download | only in Modules
      1 /*
      2  /  Author: Sam Rushing <rushing (at) 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 (at) 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 #define PY_SSIZE_T_CLEAN
     22 #include <Python.h>
     23 #include "structmember.h"
     24 
     25 #ifndef MS_WINDOWS
     26 #define UNIX
     27 # ifdef HAVE_FCNTL_H
     28 #  include <fcntl.h>
     29 # endif /* HAVE_FCNTL_H */
     30 #endif
     31 
     32 #ifdef MS_WINDOWS
     33 #include <windows.h>
     34 static int
     35 my_getpagesize(void)
     36 {
     37     SYSTEM_INFO si;
     38     GetSystemInfo(&si);
     39     return si.dwPageSize;
     40 }
     41 
     42 static int
     43 my_getallocationgranularity (void)
     44 {
     45 
     46     SYSTEM_INFO si;
     47     GetSystemInfo(&si);
     48     return si.dwAllocationGranularity;
     49 }
     50 
     51 #endif
     52 
     53 #ifdef UNIX
     54 #include <sys/mman.h>
     55 #include <sys/stat.h>
     56 
     57 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
     58 static int
     59 my_getpagesize(void)
     60 {
     61     return sysconf(_SC_PAGESIZE);
     62 }
     63 
     64 #define my_getallocationgranularity my_getpagesize
     65 #else
     66 #define my_getpagesize getpagesize
     67 #endif
     68 
     69 #endif /* UNIX */
     70 
     71 #include <string.h>
     72 
     73 #ifdef HAVE_SYS_TYPES_H
     74 #include <sys/types.h>
     75 #endif /* HAVE_SYS_TYPES_H */
     76 
     77 /* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
     78 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
     79 #  define MAP_ANONYMOUS MAP_ANON
     80 #endif
     81 
     82 typedef enum
     83 {
     84     ACCESS_DEFAULT,
     85     ACCESS_READ,
     86     ACCESS_WRITE,
     87     ACCESS_COPY
     88 } access_mode;
     89 
     90 typedef struct {
     91     PyObject_HEAD
     92     char *      data;
     93     Py_ssize_t  size;
     94     Py_ssize_t  pos;    /* relative to offset */
     95 #ifdef MS_WINDOWS
     96     long long offset;
     97 #else
     98     off_t       offset;
     99 #endif
    100     int     exports;
    101 
    102 #ifdef MS_WINDOWS
    103     HANDLE      map_handle;
    104     HANDLE      file_handle;
    105     char *      tagname;
    106 #endif
    107 
    108 #ifdef UNIX
    109     int fd;
    110 #endif
    111 
    112     PyObject *weakreflist;
    113     access_mode access;
    114 } mmap_object;
    115 
    116 
    117 static void
    118 mmap_object_dealloc(mmap_object *m_obj)
    119 {
    120 #ifdef MS_WINDOWS
    121     if (m_obj->data != NULL)
    122         UnmapViewOfFile (m_obj->data);
    123     if (m_obj->map_handle != NULL)
    124         CloseHandle (m_obj->map_handle);
    125     if (m_obj->file_handle != INVALID_HANDLE_VALUE)
    126         CloseHandle (m_obj->file_handle);
    127     if (m_obj->tagname)
    128         PyMem_Free(m_obj->tagname);
    129 #endif /* MS_WINDOWS */
    130 
    131 #ifdef UNIX
    132     if (m_obj->fd >= 0)
    133         (void) close(m_obj->fd);
    134     if (m_obj->data!=NULL) {
    135         munmap(m_obj->data, m_obj->size);
    136     }
    137 #endif /* UNIX */
    138 
    139     if (m_obj->weakreflist != NULL)
    140         PyObject_ClearWeakRefs((PyObject *) m_obj);
    141     Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
    142 }
    143 
    144 static PyObject *
    145 mmap_close_method(mmap_object *self, PyObject *unused)
    146 {
    147     if (self->exports > 0) {
    148         PyErr_SetString(PyExc_BufferError, "cannot close "\
    149                         "exported pointers exist");
    150         return NULL;
    151     }
    152 #ifdef MS_WINDOWS
    153     /* For each resource we maintain, we need to check
    154        the value is valid, and if so, free the resource
    155        and set the member value to an invalid value so
    156        the dealloc does not attempt to resource clearing
    157        again.
    158        TODO - should we check for errors in the close operations???
    159     */
    160     if (self->data != NULL) {
    161         UnmapViewOfFile(self->data);
    162         self->data = NULL;
    163     }
    164     if (self->map_handle != NULL) {
    165         CloseHandle(self->map_handle);
    166         self->map_handle = NULL;
    167     }
    168     if (self->file_handle != INVALID_HANDLE_VALUE) {
    169         CloseHandle(self->file_handle);
    170         self->file_handle = INVALID_HANDLE_VALUE;
    171     }
    172 #endif /* MS_WINDOWS */
    173 
    174 #ifdef UNIX
    175     if (0 <= self->fd)
    176         (void) close(self->fd);
    177     self->fd = -1;
    178     if (self->data != NULL) {
    179         munmap(self->data, self->size);
    180         self->data = NULL;
    181     }
    182 #endif
    183 
    184     Py_RETURN_NONE;
    185 }
    186 
    187 #ifdef MS_WINDOWS
    188 #define CHECK_VALID(err)                                                \
    189 do {                                                                    \
    190     if (self->map_handle == NULL) {                                     \
    191     PyErr_SetString(PyExc_ValueError, "mmap closed or invalid");        \
    192     return err;                                                         \
    193     }                                                                   \
    194 } while (0)
    195 #endif /* MS_WINDOWS */
    196 
    197 #ifdef UNIX
    198 #define CHECK_VALID(err)                                                \
    199 do {                                                                    \
    200     if (self->data == NULL) {                                           \
    201     PyErr_SetString(PyExc_ValueError, "mmap closed or invalid");        \
    202     return err;                                                         \
    203     }                                                                   \
    204 } while (0)
    205 #endif /* UNIX */
    206 
    207 static PyObject *
    208 mmap_read_byte_method(mmap_object *self,
    209                       PyObject *unused)
    210 {
    211     CHECK_VALID(NULL);
    212     if (self->pos >= self->size) {
    213         PyErr_SetString(PyExc_ValueError, "read byte out of range");
    214         return NULL;
    215     }
    216     return PyLong_FromLong((unsigned char)self->data[self->pos++]);
    217 }
    218 
    219 static PyObject *
    220 mmap_read_line_method(mmap_object *self,
    221                       PyObject *unused)
    222 {
    223     Py_ssize_t remaining;
    224     char *start, *eol;
    225     PyObject *result;
    226 
    227     CHECK_VALID(NULL);
    228 
    229     remaining = (self->pos < self->size) ? self->size - self->pos : 0;
    230     if (!remaining)
    231         return PyBytes_FromString("");
    232     start = self->data + self->pos;
    233     eol = memchr(start, '\n', remaining);
    234     if (!eol)
    235         eol = self->data + self->size;
    236     else
    237         ++eol; /* advance past newline */
    238     result = PyBytes_FromStringAndSize(start, (eol - start));
    239     self->pos += (eol - start);
    240     return result;
    241 }
    242 
    243 static PyObject *
    244 mmap_read_method(mmap_object *self,
    245                  PyObject *args)
    246 {
    247     Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
    248     PyObject *result;
    249 
    250     CHECK_VALID(NULL);
    251     if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
    252         return(NULL);
    253 
    254     /* silently 'adjust' out-of-range requests */
    255     remaining = (self->pos < self->size) ? self->size - self->pos : 0;
    256     if (num_bytes < 0 || num_bytes > remaining)
    257         num_bytes = remaining;
    258     result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
    259     self->pos += num_bytes;
    260     return result;
    261 }
    262 
    263 static PyObject *
    264 mmap_gfind(mmap_object *self,
    265            PyObject *args,
    266            int reverse)
    267 {
    268     Py_ssize_t start = self->pos;
    269     Py_ssize_t end = self->size;
    270     Py_buffer view;
    271 
    272     CHECK_VALID(NULL);
    273     if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
    274                           &view, &start, &end)) {
    275         return NULL;
    276     } else {
    277         const char *p, *start_p, *end_p;
    278         int sign = reverse ? -1 : 1;
    279         const char *needle = view.buf;
    280         Py_ssize_t len = view.len;
    281 
    282         if (start < 0)
    283             start += self->size;
    284         if (start < 0)
    285             start = 0;
    286         else if (start > self->size)
    287             start = self->size;
    288 
    289         if (end < 0)
    290             end += self->size;
    291         if (end < 0)
    292             end = 0;
    293         else if (end > self->size)
    294             end = self->size;
    295 
    296         start_p = self->data + start;
    297         end_p = self->data + end;
    298 
    299         for (p = (reverse ? end_p - len : start_p);
    300              (p >= start_p) && (p + len <= end_p); p += sign) {
    301             Py_ssize_t i;
    302             for (i = 0; i < len && needle[i] == p[i]; ++i)
    303                 /* nothing */;
    304             if (i == len) {
    305                 PyBuffer_Release(&view);
    306                 return PyLong_FromSsize_t(p - self->data);
    307             }
    308         }
    309         PyBuffer_Release(&view);
    310         return PyLong_FromLong(-1);
    311     }
    312 }
    313 
    314 static PyObject *
    315 mmap_find_method(mmap_object *self,
    316                  PyObject *args)
    317 {
    318     return mmap_gfind(self, args, 0);
    319 }
    320 
    321 static PyObject *
    322 mmap_rfind_method(mmap_object *self,
    323                  PyObject *args)
    324 {
    325     return mmap_gfind(self, args, 1);
    326 }
    327 
    328 static int
    329 is_writable(mmap_object *self)
    330 {
    331     if (self->access != ACCESS_READ)
    332         return 1;
    333     PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
    334     return 0;
    335 }
    336 
    337 static int
    338 is_resizeable(mmap_object *self)
    339 {
    340     if (self->exports > 0) {
    341         PyErr_SetString(PyExc_BufferError,
    342                         "mmap can't resize with extant buffers exported.");
    343         return 0;
    344     }
    345     if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
    346         return 1;
    347     PyErr_Format(PyExc_TypeError,
    348                  "mmap can't resize a readonly or copy-on-write memory map.");
    349     return 0;
    350 }
    351 
    352 
    353 static PyObject *
    354 mmap_write_method(mmap_object *self,
    355                   PyObject *args)
    356 {
    357     Py_buffer data;
    358 
    359     CHECK_VALID(NULL);
    360     if (!PyArg_ParseTuple(args, "y*:write", &data))
    361         return(NULL);
    362 
    363     if (!is_writable(self)) {
    364         PyBuffer_Release(&data);
    365         return NULL;
    366     }
    367 
    368     if (self->pos > self->size || self->size - self->pos < data.len) {
    369         PyBuffer_Release(&data);
    370         PyErr_SetString(PyExc_ValueError, "data out of range");
    371         return NULL;
    372     }
    373 
    374     memcpy(&self->data[self->pos], data.buf, data.len);
    375     self->pos += data.len;
    376     PyBuffer_Release(&data);
    377     return PyLong_FromSsize_t(data.len);
    378 }
    379 
    380 static PyObject *
    381 mmap_write_byte_method(mmap_object *self,
    382                        PyObject *args)
    383 {
    384     char value;
    385 
    386     CHECK_VALID(NULL);
    387     if (!PyArg_ParseTuple(args, "b:write_byte", &value))
    388         return(NULL);
    389 
    390     if (!is_writable(self))
    391         return NULL;
    392 
    393     if (self->pos < self->size) {
    394         self->data[self->pos++] = value;
    395         Py_RETURN_NONE;
    396     }
    397     else {
    398         PyErr_SetString(PyExc_ValueError, "write byte out of range");
    399         return NULL;
    400     }
    401 }
    402 
    403 static PyObject *
    404 mmap_size_method(mmap_object *self,
    405                  PyObject *unused)
    406 {
    407     CHECK_VALID(NULL);
    408 
    409 #ifdef MS_WINDOWS
    410     if (self->file_handle != INVALID_HANDLE_VALUE) {
    411         DWORD low,high;
    412         long long size;
    413         low = GetFileSize(self->file_handle, &high);
    414         if (low == INVALID_FILE_SIZE) {
    415             /* It might be that the function appears to have failed,
    416                when indeed its size equals INVALID_FILE_SIZE */
    417             DWORD error = GetLastError();
    418             if (error != NO_ERROR)
    419                 return PyErr_SetFromWindowsErr(error);
    420         }
    421         if (!high && low < LONG_MAX)
    422             return PyLong_FromLong((long)low);
    423         size = (((long long)high)<<32) + low;
    424         return PyLong_FromLongLong(size);
    425     } else {
    426         return PyLong_FromSsize_t(self->size);
    427     }
    428 #endif /* MS_WINDOWS */
    429 
    430 #ifdef UNIX
    431     {
    432         struct _Py_stat_struct status;
    433         if (_Py_fstat(self->fd, &status) == -1)
    434             return NULL;
    435 #ifdef HAVE_LARGEFILE_SUPPORT
    436         return PyLong_FromLongLong(status.st_size);
    437 #else
    438         return PyLong_FromLong(status.st_size);
    439 #endif
    440     }
    441 #endif /* UNIX */
    442 }
    443 
    444 /* This assumes that you want the entire file mapped,
    445  / and when recreating the map will make the new file
    446  / have the new size
    447  /
    448  / Is this really necessary?  This could easily be done
    449  / from python by just closing and re-opening with the
    450  / new size?
    451  */
    452 
    453 static PyObject *
    454 mmap_resize_method(mmap_object *self,
    455                    PyObject *args)
    456 {
    457     Py_ssize_t new_size;
    458     CHECK_VALID(NULL);
    459     if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
    460         !is_resizeable(self)) {
    461         return NULL;
    462     }
    463     if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
    464         PyErr_SetString(PyExc_ValueError, "new size out of range");
    465         return NULL;
    466     }
    467 
    468     {
    469 #ifdef MS_WINDOWS
    470         DWORD dwErrCode = 0;
    471         DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
    472         /* First, unmap the file view */
    473         UnmapViewOfFile(self->data);
    474         self->data = NULL;
    475         /* Close the mapping object */
    476         CloseHandle(self->map_handle);
    477         self->map_handle = NULL;
    478         /* Move to the desired EOF position */
    479         newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
    480         newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
    481         off_hi = (DWORD)(self->offset >> 32);
    482         off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
    483         SetFilePointer(self->file_handle,
    484                        newSizeLow, &newSizeHigh, FILE_BEGIN);
    485         /* Change the size of the file */
    486         SetEndOfFile(self->file_handle);
    487         /* Create another mapping object and remap the file view */
    488         self->map_handle = CreateFileMapping(
    489             self->file_handle,
    490             NULL,
    491             PAGE_READWRITE,
    492             0,
    493             0,
    494             self->tagname);
    495         if (self->map_handle != NULL) {
    496             self->data = (char *) MapViewOfFile(self->map_handle,
    497                                                 FILE_MAP_WRITE,
    498                                                 off_hi,
    499                                                 off_lo,
    500                                                 new_size);
    501             if (self->data != NULL) {
    502                 self->size = new_size;
    503                 Py_RETURN_NONE;
    504             } else {
    505                 dwErrCode = GetLastError();
    506                 CloseHandle(self->map_handle);
    507                 self->map_handle = NULL;
    508             }
    509         } else {
    510             dwErrCode = GetLastError();
    511         }
    512         PyErr_SetFromWindowsErr(dwErrCode);
    513         return NULL;
    514 #endif /* MS_WINDOWS */
    515 
    516 #ifdef UNIX
    517 #ifndef HAVE_MREMAP
    518         PyErr_SetString(PyExc_SystemError,
    519                         "mmap: resizing not available--no mremap()");
    520         return NULL;
    521 #else
    522         void *newmap;
    523 
    524         if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
    525             PyErr_SetFromErrno(PyExc_OSError);
    526             return NULL;
    527         }
    528 
    529 #ifdef MREMAP_MAYMOVE
    530         newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
    531 #else
    532 #if defined(__NetBSD__)
    533         newmap = mremap(self->data, self->size, self->data, new_size, 0);
    534 #else
    535         newmap = mremap(self->data, self->size, new_size, 0);
    536 #endif /* __NetBSD__ */
    537 #endif
    538         if (newmap == (void *)-1)
    539         {
    540             PyErr_SetFromErrno(PyExc_OSError);
    541             return NULL;
    542         }
    543         self->data = newmap;
    544         self->size = new_size;
    545         Py_RETURN_NONE;
    546 #endif /* HAVE_MREMAP */
    547 #endif /* UNIX */
    548     }
    549 }
    550 
    551 static PyObject *
    552 mmap_tell_method(mmap_object *self, PyObject *unused)
    553 {
    554     CHECK_VALID(NULL);
    555     return PyLong_FromSize_t(self->pos);
    556 }
    557 
    558 static PyObject *
    559 mmap_flush_method(mmap_object *self, PyObject *args)
    560 {
    561     Py_ssize_t offset = 0;
    562     Py_ssize_t size = self->size;
    563     CHECK_VALID(NULL);
    564     if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
    565         return NULL;
    566     if (size < 0 || offset < 0 || self->size - offset < size) {
    567         PyErr_SetString(PyExc_ValueError, "flush values out of range");
    568         return NULL;
    569     }
    570 
    571     if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
    572         return PyLong_FromLong(0);
    573 
    574 #ifdef MS_WINDOWS
    575     return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
    576 #elif defined(UNIX)
    577     /* XXX semantics of return value? */
    578     /* XXX flags for msync? */
    579     if (-1 == msync(self->data + offset, size, MS_SYNC)) {
    580         PyErr_SetFromErrno(PyExc_OSError);
    581         return NULL;
    582     }
    583     return PyLong_FromLong(0);
    584 #else
    585     PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
    586     return NULL;
    587 #endif
    588 }
    589 
    590 static PyObject *
    591 mmap_seek_method(mmap_object *self, PyObject *args)
    592 {
    593     Py_ssize_t dist;
    594     int how=0;
    595     CHECK_VALID(NULL);
    596     if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
    597         return NULL;
    598     else {
    599         Py_ssize_t where;
    600         switch (how) {
    601         case 0: /* relative to start */
    602             where = dist;
    603             break;
    604         case 1: /* relative to current position */
    605             if (PY_SSIZE_T_MAX - self->pos < dist)
    606                 goto onoutofrange;
    607             where = self->pos + dist;
    608             break;
    609         case 2: /* relative to end */
    610             if (PY_SSIZE_T_MAX - self->size < dist)
    611                 goto onoutofrange;
    612             where = self->size + dist;
    613             break;
    614         default:
    615             PyErr_SetString(PyExc_ValueError, "unknown seek type");
    616             return NULL;
    617         }
    618         if (where > self->size || where < 0)
    619             goto onoutofrange;
    620         self->pos = where;
    621         Py_RETURN_NONE;
    622     }
    623 
    624   onoutofrange:
    625     PyErr_SetString(PyExc_ValueError, "seek out of range");
    626     return NULL;
    627 }
    628 
    629 static PyObject *
    630 mmap_move_method(mmap_object *self, PyObject *args)
    631 {
    632     Py_ssize_t dest, src, cnt;
    633     CHECK_VALID(NULL);
    634     if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
    635         !is_writable(self)) {
    636         return NULL;
    637     } else {
    638         /* bounds check the values */
    639         if (dest < 0 || src < 0 || cnt < 0)
    640             goto bounds;
    641         if (self->size - dest < cnt || self->size - src < cnt)
    642             goto bounds;
    643 
    644         memmove(&self->data[dest], &self->data[src], cnt);
    645 
    646         Py_RETURN_NONE;
    647 
    648       bounds:
    649         PyErr_SetString(PyExc_ValueError,
    650                         "source, destination, or count out of range");
    651         return NULL;
    652     }
    653 }
    654 
    655 static PyObject *
    656 mmap_closed_get(mmap_object *self, void *Py_UNUSED(ignored))
    657 {
    658 #ifdef MS_WINDOWS
    659     return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
    660 #elif defined(UNIX)
    661     return PyBool_FromLong(self->data == NULL ? 1 : 0);
    662 #endif
    663 }
    664 
    665 static PyObject *
    666 mmap__enter__method(mmap_object *self, PyObject *args)
    667 {
    668     CHECK_VALID(NULL);
    669 
    670     Py_INCREF(self);
    671     return (PyObject *)self;
    672 }
    673 
    674 static PyObject *
    675 mmap__exit__method(PyObject *self, PyObject *args)
    676 {
    677     _Py_IDENTIFIER(close);
    678 
    679     return _PyObject_CallMethodId(self, &PyId_close, NULL);
    680 }
    681 
    682 #ifdef MS_WINDOWS
    683 static PyObject *
    684 mmap__sizeof__method(mmap_object *self, void *unused)
    685 {
    686     Py_ssize_t res;
    687 
    688     res = _PyObject_SIZE(Py_TYPE(self));
    689     if (self->tagname)
    690         res += strlen(self->tagname) + 1;
    691     return PyLong_FromSsize_t(res);
    692 }
    693 #endif
    694 
    695 static struct PyMethodDef mmap_object_methods[] = {
    696     {"close",           (PyCFunction) mmap_close_method,        METH_NOARGS},
    697     {"find",            (PyCFunction) mmap_find_method,         METH_VARARGS},
    698     {"rfind",           (PyCFunction) mmap_rfind_method,        METH_VARARGS},
    699     {"flush",           (PyCFunction) mmap_flush_method,        METH_VARARGS},
    700     {"move",            (PyCFunction) mmap_move_method,         METH_VARARGS},
    701     {"read",            (PyCFunction) mmap_read_method,         METH_VARARGS},
    702     {"read_byte",       (PyCFunction) mmap_read_byte_method,    METH_NOARGS},
    703     {"readline",        (PyCFunction) mmap_read_line_method,    METH_NOARGS},
    704     {"resize",          (PyCFunction) mmap_resize_method,       METH_VARARGS},
    705     {"seek",            (PyCFunction) mmap_seek_method,         METH_VARARGS},
    706     {"size",            (PyCFunction) mmap_size_method,         METH_NOARGS},
    707     {"tell",            (PyCFunction) mmap_tell_method,         METH_NOARGS},
    708     {"write",           (PyCFunction) mmap_write_method,        METH_VARARGS},
    709     {"write_byte",      (PyCFunction) mmap_write_byte_method,   METH_VARARGS},
    710     {"__enter__",       (PyCFunction) mmap__enter__method,      METH_NOARGS},
    711     {"__exit__",        (PyCFunction) mmap__exit__method,       METH_VARARGS},
    712 #ifdef MS_WINDOWS
    713     {"__sizeof__",      (PyCFunction) mmap__sizeof__method,     METH_NOARGS},
    714 #endif
    715     {NULL,         NULL}       /* sentinel */
    716 };
    717 
    718 static PyGetSetDef mmap_object_getset[] = {
    719     {"closed", (getter) mmap_closed_get, NULL, NULL},
    720     {NULL}
    721 };
    722 
    723 
    724 /* Functions for treating an mmap'ed file as a buffer */
    725 
    726 static int
    727 mmap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
    728 {
    729     CHECK_VALID(-1);
    730     if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
    731                           (self->access == ACCESS_READ), flags) < 0)
    732         return -1;
    733     self->exports++;
    734     return 0;
    735 }
    736 
    737 static void
    738 mmap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
    739 {
    740     self->exports--;
    741 }
    742 
    743 static Py_ssize_t
    744 mmap_length(mmap_object *self)
    745 {
    746     CHECK_VALID(-1);
    747     return self->size;
    748 }
    749 
    750 static PyObject *
    751 mmap_item(mmap_object *self, Py_ssize_t i)
    752 {
    753     CHECK_VALID(NULL);
    754     if (i < 0 || i >= self->size) {
    755         PyErr_SetString(PyExc_IndexError, "mmap index out of range");
    756         return NULL;
    757     }
    758     return PyBytes_FromStringAndSize(self->data + i, 1);
    759 }
    760 
    761 static PyObject *
    762 mmap_subscript(mmap_object *self, PyObject *item)
    763 {
    764     CHECK_VALID(NULL);
    765     if (PyIndex_Check(item)) {
    766         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
    767         if (i == -1 && PyErr_Occurred())
    768             return NULL;
    769         if (i < 0)
    770             i += self->size;
    771         if (i < 0 || i >= self->size) {
    772             PyErr_SetString(PyExc_IndexError,
    773                 "mmap index out of range");
    774             return NULL;
    775         }
    776         return PyLong_FromLong(Py_CHARMASK(self->data[i]));
    777     }
    778     else if (PySlice_Check(item)) {
    779         Py_ssize_t start, stop, step, slicelen;
    780 
    781         if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
    782             return NULL;
    783         }
    784         slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
    785 
    786         if (slicelen <= 0)
    787             return PyBytes_FromStringAndSize("", 0);
    788         else if (step == 1)
    789             return PyBytes_FromStringAndSize(self->data + start,
    790                                               slicelen);
    791         else {
    792             char *result_buf = (char *)PyMem_Malloc(slicelen);
    793             Py_ssize_t cur, i;
    794             PyObject *result;
    795 
    796             if (result_buf == NULL)
    797                 return PyErr_NoMemory();
    798             for (cur = start, i = 0; i < slicelen;
    799                  cur += step, i++) {
    800                 result_buf[i] = self->data[cur];
    801             }
    802             result = PyBytes_FromStringAndSize(result_buf,
    803                                                 slicelen);
    804             PyMem_Free(result_buf);
    805             return result;
    806         }
    807     }
    808     else {
    809         PyErr_SetString(PyExc_TypeError,
    810                         "mmap indices must be integers");
    811         return NULL;
    812     }
    813 }
    814 
    815 static int
    816 mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
    817 {
    818     const char *buf;
    819 
    820     CHECK_VALID(-1);
    821     if (i < 0 || i >= self->size) {
    822         PyErr_SetString(PyExc_IndexError, "mmap index out of range");
    823         return -1;
    824     }
    825     if (v == NULL) {
    826         PyErr_SetString(PyExc_TypeError,
    827                         "mmap object doesn't support item deletion");
    828         return -1;
    829     }
    830     if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
    831         PyErr_SetString(PyExc_IndexError,
    832                         "mmap assignment must be length-1 bytes()");
    833         return -1;
    834     }
    835     if (!is_writable(self))
    836         return -1;
    837     buf = PyBytes_AsString(v);
    838     self->data[i] = buf[0];
    839     return 0;
    840 }
    841 
    842 static int
    843 mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
    844 {
    845     CHECK_VALID(-1);
    846 
    847     if (!is_writable(self))
    848         return -1;
    849 
    850     if (PyIndex_Check(item)) {
    851         Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
    852         Py_ssize_t v;
    853 
    854         if (i == -1 && PyErr_Occurred())
    855             return -1;
    856         if (i < 0)
    857             i += self->size;
    858         if (i < 0 || i >= self->size) {
    859             PyErr_SetString(PyExc_IndexError,
    860                             "mmap index out of range");
    861             return -1;
    862         }
    863         if (value == NULL) {
    864             PyErr_SetString(PyExc_TypeError,
    865                             "mmap doesn't support item deletion");
    866             return -1;
    867         }
    868         if (!PyIndex_Check(value)) {
    869             PyErr_SetString(PyExc_TypeError,
    870                             "mmap item value must be an int");
    871             return -1;
    872         }
    873         v = PyNumber_AsSsize_t(value, PyExc_TypeError);
    874         if (v == -1 && PyErr_Occurred())
    875             return -1;
    876         if (v < 0 || v > 255) {
    877             PyErr_SetString(PyExc_ValueError,
    878                             "mmap item value must be "
    879                             "in range(0, 256)");
    880             return -1;
    881         }
    882         self->data[i] = (char) v;
    883         return 0;
    884     }
    885     else if (PySlice_Check(item)) {
    886         Py_ssize_t start, stop, step, slicelen;
    887         Py_buffer vbuf;
    888 
    889         if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
    890             return -1;
    891         }
    892         slicelen = PySlice_AdjustIndices(self->size, &start, &stop, step);
    893         if (value == NULL) {
    894             PyErr_SetString(PyExc_TypeError,
    895                 "mmap object doesn't support slice deletion");
    896             return -1;
    897         }
    898         if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
    899             return -1;
    900         if (vbuf.len != slicelen) {
    901             PyErr_SetString(PyExc_IndexError,
    902                 "mmap slice assignment is wrong size");
    903             PyBuffer_Release(&vbuf);
    904             return -1;
    905         }
    906 
    907         if (slicelen == 0) {
    908         }
    909         else if (step == 1) {
    910             memcpy(self->data + start, vbuf.buf, slicelen);
    911         }
    912         else {
    913             Py_ssize_t cur, i;
    914 
    915             for (cur = start, i = 0;
    916                  i < slicelen;
    917                  cur += step, i++)
    918             {
    919                 self->data[cur] = ((char *)vbuf.buf)[i];
    920             }
    921         }
    922         PyBuffer_Release(&vbuf);
    923         return 0;
    924     }
    925     else {
    926         PyErr_SetString(PyExc_TypeError,
    927                         "mmap indices must be integer");
    928         return -1;
    929     }
    930 }
    931 
    932 static PySequenceMethods mmap_as_sequence = {
    933     (lenfunc)mmap_length,            /*sq_length*/
    934     0,                               /*sq_concat*/
    935     0,                               /*sq_repeat*/
    936     (ssizeargfunc)mmap_item,         /*sq_item*/
    937     0,                               /*sq_slice*/
    938     (ssizeobjargproc)mmap_ass_item,  /*sq_ass_item*/
    939     0,                               /*sq_ass_slice*/
    940 };
    941 
    942 static PyMappingMethods mmap_as_mapping = {
    943     (lenfunc)mmap_length,
    944     (binaryfunc)mmap_subscript,
    945     (objobjargproc)mmap_ass_subscript,
    946 };
    947 
    948 static PyBufferProcs mmap_as_buffer = {
    949     (getbufferproc)mmap_buffer_getbuf,
    950     (releasebufferproc)mmap_buffer_releasebuf,
    951 };
    952 
    953 static PyObject *
    954 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
    955 
    956 PyDoc_STRVAR(mmap_doc,
    957 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
    958 \n\
    959 Maps length bytes from the file specified by the file handle fileno,\n\
    960 and returns a mmap object.  If length is larger than the current size\n\
    961 of the file, the file is extended to contain length bytes.  If length\n\
    962 is 0, the maximum length of the map is the current size of the file,\n\
    963 except that if the file is empty Windows raises an exception (you cannot\n\
    964 create an empty mapping on Windows).\n\
    965 \n\
    966 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
    967 \n\
    968 Maps length bytes from the file specified by the file descriptor fileno,\n\
    969 and returns a mmap object.  If length is 0, the maximum length of the map\n\
    970 will be the current size of the file when mmap is called.\n\
    971 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
    972 private copy-on-write mapping, so changes to the contents of the mmap\n\
    973 object will be private to this process, and MAP_SHARED creates a mapping\n\
    974 that's shared with all other processes mapping the same areas of the file.\n\
    975 The default value is MAP_SHARED.\n\
    976 \n\
    977 To map anonymous memory, pass -1 as the fileno (both versions).");
    978 
    979 
    980 static PyTypeObject mmap_object_type = {
    981     PyVarObject_HEAD_INIT(NULL, 0)
    982     "mmap.mmap",                                /* tp_name */
    983     sizeof(mmap_object),                        /* tp_basicsize */
    984     0,                                          /* tp_itemsize */
    985     /* methods */
    986     (destructor) mmap_object_dealloc,           /* tp_dealloc */
    987     0,                                          /* tp_print */
    988     0,                                          /* tp_getattr */
    989     0,                                          /* tp_setattr */
    990     0,                                          /* tp_reserved */
    991     0,                                          /* tp_repr */
    992     0,                                          /* tp_as_number */
    993     &mmap_as_sequence,                          /*tp_as_sequence*/
    994     &mmap_as_mapping,                           /*tp_as_mapping*/
    995     0,                                          /*tp_hash*/
    996     0,                                          /*tp_call*/
    997     0,                                          /*tp_str*/
    998     PyObject_GenericGetAttr,                    /*tp_getattro*/
    999     0,                                          /*tp_setattro*/
   1000     &mmap_as_buffer,                            /*tp_as_buffer*/
   1001     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /*tp_flags*/
   1002     mmap_doc,                                   /*tp_doc*/
   1003     0,                                          /* tp_traverse */
   1004     0,                                          /* tp_clear */
   1005     0,                                          /* tp_richcompare */
   1006     offsetof(mmap_object, weakreflist),         /* tp_weaklistoffset */
   1007     0,                                          /* tp_iter */
   1008     0,                                          /* tp_iternext */
   1009     mmap_object_methods,                        /* tp_methods */
   1010     0,                                          /* tp_members */
   1011     mmap_object_getset,                         /* tp_getset */
   1012     0,                                          /* tp_base */
   1013     0,                                          /* tp_dict */
   1014     0,                                          /* tp_descr_get */
   1015     0,                                          /* tp_descr_set */
   1016     0,                                          /* tp_dictoffset */
   1017     0,                                          /* tp_init */
   1018     PyType_GenericAlloc,                        /* tp_alloc */
   1019     new_mmap_object,                            /* tp_new */
   1020     PyObject_Del,                               /* tp_free */
   1021 };
   1022 
   1023 
   1024 #ifdef UNIX
   1025 #ifdef HAVE_LARGEFILE_SUPPORT
   1026 #define _Py_PARSE_OFF_T "L"
   1027 #else
   1028 #define _Py_PARSE_OFF_T "l"
   1029 #endif
   1030 
   1031 static PyObject *
   1032 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
   1033 {
   1034     struct _Py_stat_struct status;
   1035     int fstat_result = -1;
   1036     mmap_object *m_obj;
   1037     Py_ssize_t map_size;
   1038     off_t offset = 0;
   1039     int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
   1040     int devzero = -1;
   1041     int access = (int)ACCESS_DEFAULT;
   1042     static char *keywords[] = {"fileno", "length",
   1043                                "flags", "prot",
   1044                                "access", "offset", NULL};
   1045 
   1046     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
   1047                                      &fd, &map_size, &flags, &prot,
   1048                                      &access, &offset))
   1049         return NULL;
   1050     if (map_size < 0) {
   1051         PyErr_SetString(PyExc_OverflowError,
   1052                         "memory mapped length must be positive");
   1053         return NULL;
   1054     }
   1055     if (offset < 0) {
   1056         PyErr_SetString(PyExc_OverflowError,
   1057             "memory mapped offset must be positive");
   1058         return NULL;
   1059     }
   1060 
   1061     if ((access != (int)ACCESS_DEFAULT) &&
   1062         ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
   1063         return PyErr_Format(PyExc_ValueError,
   1064                             "mmap can't specify both access and flags, prot.");
   1065     switch ((access_mode)access) {
   1066     case ACCESS_READ:
   1067         flags = MAP_SHARED;
   1068         prot = PROT_READ;
   1069         break;
   1070     case ACCESS_WRITE:
   1071         flags = MAP_SHARED;
   1072         prot = PROT_READ | PROT_WRITE;
   1073         break;
   1074     case ACCESS_COPY:
   1075         flags = MAP_PRIVATE;
   1076         prot = PROT_READ | PROT_WRITE;
   1077         break;
   1078     case ACCESS_DEFAULT:
   1079         /* map prot to access type */
   1080         if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
   1081             /* ACCESS_DEFAULT */
   1082         }
   1083         else if (prot & PROT_WRITE) {
   1084             access = ACCESS_WRITE;
   1085         }
   1086         else {
   1087             access = ACCESS_READ;
   1088         }
   1089         break;
   1090     default:
   1091         return PyErr_Format(PyExc_ValueError,
   1092                             "mmap invalid access parameter.");
   1093     }
   1094 
   1095 #ifdef __APPLE__
   1096     /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
   1097        fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
   1098     if (fd != -1)
   1099         (void)fcntl(fd, F_FULLFSYNC);
   1100 #endif
   1101 
   1102     if (fd != -1) {
   1103         Py_BEGIN_ALLOW_THREADS
   1104         fstat_result = _Py_fstat_noraise(fd, &status);
   1105         Py_END_ALLOW_THREADS
   1106     }
   1107 
   1108     if (fd != -1 && fstat_result == 0 && S_ISREG(status.st_mode)) {
   1109         if (map_size == 0) {
   1110             if (status.st_size == 0) {
   1111                 PyErr_SetString(PyExc_ValueError,
   1112                                 "cannot mmap an empty file");
   1113                 return NULL;
   1114             }
   1115             if (offset >= status.st_size) {
   1116                 PyErr_SetString(PyExc_ValueError,
   1117                                 "mmap offset is greater than file size");
   1118                 return NULL;
   1119             }
   1120             if (status.st_size - offset > PY_SSIZE_T_MAX) {
   1121                 PyErr_SetString(PyExc_ValueError,
   1122                                  "mmap length is too large");
   1123                 return NULL;
   1124             }
   1125             map_size = (Py_ssize_t) (status.st_size - offset);
   1126         } else if (offset > status.st_size || status.st_size - offset < map_size) {
   1127             PyErr_SetString(PyExc_ValueError,
   1128                             "mmap length is greater than file size");
   1129             return NULL;
   1130         }
   1131     }
   1132     m_obj = (mmap_object *)type->tp_alloc(type, 0);
   1133     if (m_obj == NULL) {return NULL;}
   1134     m_obj->data = NULL;
   1135     m_obj->size = map_size;
   1136     m_obj->pos = 0;
   1137     m_obj->weakreflist = NULL;
   1138     m_obj->exports = 0;
   1139     m_obj->offset = offset;
   1140     if (fd == -1) {
   1141         m_obj->fd = -1;
   1142         /* Assume the caller wants to map anonymous memory.
   1143            This is the same behaviour as Windows.  mmap.mmap(-1, size)
   1144            on both Windows and Unix map anonymous memory.
   1145         */
   1146 #ifdef MAP_ANONYMOUS
   1147         /* BSD way to map anonymous memory */
   1148         flags |= MAP_ANONYMOUS;
   1149 #else
   1150         /* SVR4 method to map anonymous memory is to open /dev/zero */
   1151         fd = devzero = _Py_open("/dev/zero", O_RDWR);
   1152         if (devzero == -1) {
   1153             Py_DECREF(m_obj);
   1154             return NULL;
   1155         }
   1156 #endif
   1157     }
   1158     else {
   1159         m_obj->fd = _Py_dup(fd);
   1160         if (m_obj->fd == -1) {
   1161             Py_DECREF(m_obj);
   1162             return NULL;
   1163         }
   1164     }
   1165 
   1166     m_obj->data = mmap(NULL, map_size,
   1167                        prot, flags,
   1168                        fd, offset);
   1169 
   1170     if (devzero != -1) {
   1171         close(devzero);
   1172     }
   1173 
   1174     if (m_obj->data == (char *)-1) {
   1175         m_obj->data = NULL;
   1176         Py_DECREF(m_obj);
   1177         PyErr_SetFromErrno(PyExc_OSError);
   1178         return NULL;
   1179     }
   1180     m_obj->access = (access_mode)access;
   1181     return (PyObject *)m_obj;
   1182 }
   1183 #endif /* UNIX */
   1184 
   1185 #ifdef MS_WINDOWS
   1186 
   1187 /* A note on sizes and offsets: while the actual map size must hold in a
   1188    Py_ssize_t, both the total file size and the start offset can be longer
   1189    than a Py_ssize_t, so we use long long which is always 64-bit.
   1190 */
   1191 
   1192 static PyObject *
   1193 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
   1194 {
   1195     mmap_object *m_obj;
   1196     Py_ssize_t map_size;
   1197     long long offset = 0, size;
   1198     DWORD off_hi;       /* upper 32 bits of offset */
   1199     DWORD off_lo;       /* lower 32 bits of offset */
   1200     DWORD size_hi;      /* upper 32 bits of size */
   1201     DWORD size_lo;      /* lower 32 bits of size */
   1202     const char *tagname = "";
   1203     DWORD dwErr = 0;
   1204     int fileno;
   1205     HANDLE fh = 0;
   1206     int access = (access_mode)ACCESS_DEFAULT;
   1207     DWORD flProtect, dwDesiredAccess;
   1208     static char *keywords[] = { "fileno", "length",
   1209                                 "tagname",
   1210                                 "access", "offset", NULL };
   1211 
   1212     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
   1213                                      &fileno, &map_size,
   1214                                      &tagname, &access, &offset)) {
   1215         return NULL;
   1216     }
   1217 
   1218     switch((access_mode)access) {
   1219     case ACCESS_READ:
   1220         flProtect = PAGE_READONLY;
   1221         dwDesiredAccess = FILE_MAP_READ;
   1222         break;
   1223     case ACCESS_DEFAULT:  case ACCESS_WRITE:
   1224         flProtect = PAGE_READWRITE;
   1225         dwDesiredAccess = FILE_MAP_WRITE;
   1226         break;
   1227     case ACCESS_COPY:
   1228         flProtect = PAGE_WRITECOPY;
   1229         dwDesiredAccess = FILE_MAP_COPY;
   1230         break;
   1231     default:
   1232         return PyErr_Format(PyExc_ValueError,
   1233                             "mmap invalid access parameter.");
   1234     }
   1235 
   1236     if (map_size < 0) {
   1237         PyErr_SetString(PyExc_OverflowError,
   1238                         "memory mapped length must be positive");
   1239         return NULL;
   1240     }
   1241     if (offset < 0) {
   1242         PyErr_SetString(PyExc_OverflowError,
   1243             "memory mapped offset must be positive");
   1244         return NULL;
   1245     }
   1246 
   1247     /* assume -1 and 0 both mean invalid filedescriptor
   1248        to 'anonymously' map memory.
   1249        XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
   1250        XXX: Should this code be added?
   1251        if (fileno == 0)
   1252         PyErr_WarnEx(PyExc_DeprecationWarning,
   1253                      "don't use 0 for anonymous memory",
   1254                      1);
   1255      */
   1256     if (fileno != -1 && fileno != 0) {
   1257         /* Ensure that fileno is within the CRT's valid range */
   1258         _Py_BEGIN_SUPPRESS_IPH
   1259         fh = (HANDLE)_get_osfhandle(fileno);
   1260         _Py_END_SUPPRESS_IPH
   1261         if (fh==(HANDLE)-1) {
   1262             PyErr_SetFromErrno(PyExc_OSError);
   1263             return NULL;
   1264         }
   1265         /* Win9x appears to need us seeked to zero */
   1266         lseek(fileno, 0, SEEK_SET);
   1267     }
   1268 
   1269     m_obj = (mmap_object *)type->tp_alloc(type, 0);
   1270     if (m_obj == NULL)
   1271         return NULL;
   1272     /* Set every field to an invalid marker, so we can safely
   1273        destruct the object in the face of failure */
   1274     m_obj->data = NULL;
   1275     m_obj->file_handle = INVALID_HANDLE_VALUE;
   1276     m_obj->map_handle = NULL;
   1277     m_obj->tagname = NULL;
   1278     m_obj->offset = offset;
   1279 
   1280     if (fh) {
   1281         /* It is necessary to duplicate the handle, so the
   1282            Python code can close it on us */
   1283         if (!DuplicateHandle(
   1284             GetCurrentProcess(), /* source process handle */
   1285             fh, /* handle to be duplicated */
   1286             GetCurrentProcess(), /* target proc handle */
   1287             (LPHANDLE)&m_obj->file_handle, /* result */
   1288             0, /* access - ignored due to options value */
   1289             FALSE, /* inherited by child processes? */
   1290             DUPLICATE_SAME_ACCESS)) { /* options */
   1291             dwErr = GetLastError();
   1292             Py_DECREF(m_obj);
   1293             PyErr_SetFromWindowsErr(dwErr);
   1294             return NULL;
   1295         }
   1296         if (!map_size) {
   1297             DWORD low,high;
   1298             low = GetFileSize(fh, &high);
   1299             /* low might just happen to have the value INVALID_FILE_SIZE;
   1300                so we need to check the last error also. */
   1301             if (low == INVALID_FILE_SIZE &&
   1302                 (dwErr = GetLastError()) != NO_ERROR) {
   1303                 Py_DECREF(m_obj);
   1304                 return PyErr_SetFromWindowsErr(dwErr);
   1305             }
   1306 
   1307             size = (((long long) high) << 32) + low;
   1308             if (size == 0) {
   1309                 PyErr_SetString(PyExc_ValueError,
   1310                                 "cannot mmap an empty file");
   1311                 Py_DECREF(m_obj);
   1312                 return NULL;
   1313             }
   1314             if (offset >= size) {
   1315                 PyErr_SetString(PyExc_ValueError,
   1316                                 "mmap offset is greater than file size");
   1317                 Py_DECREF(m_obj);
   1318                 return NULL;
   1319             }
   1320             if (size - offset > PY_SSIZE_T_MAX) {
   1321                 PyErr_SetString(PyExc_ValueError,
   1322                                 "mmap length is too large");
   1323                 Py_DECREF(m_obj);
   1324                 return NULL;
   1325             }
   1326             m_obj->size = (Py_ssize_t) (size - offset);
   1327         } else {
   1328             m_obj->size = map_size;
   1329             size = offset + map_size;
   1330         }
   1331     }
   1332     else {
   1333         m_obj->size = map_size;
   1334         size = offset + map_size;
   1335     }
   1336 
   1337     /* set the initial position */
   1338     m_obj->pos = (size_t) 0;
   1339 
   1340     m_obj->weakreflist = NULL;
   1341     m_obj->exports = 0;
   1342     /* set the tag name */
   1343     if (tagname != NULL && *tagname != '\0') {
   1344         m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
   1345         if (m_obj->tagname == NULL) {
   1346             PyErr_NoMemory();
   1347             Py_DECREF(m_obj);
   1348             return NULL;
   1349         }
   1350         strcpy(m_obj->tagname, tagname);
   1351     }
   1352     else
   1353         m_obj->tagname = NULL;
   1354 
   1355     m_obj->access = (access_mode)access;
   1356     size_hi = (DWORD)(size >> 32);
   1357     size_lo = (DWORD)(size & 0xFFFFFFFF);
   1358     off_hi = (DWORD)(offset >> 32);
   1359     off_lo = (DWORD)(offset & 0xFFFFFFFF);
   1360     /* For files, it would be sufficient to pass 0 as size.
   1361        For anonymous maps, we have to pass the size explicitly. */
   1362     m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
   1363                                           NULL,
   1364                                           flProtect,
   1365                                           size_hi,
   1366                                           size_lo,
   1367                                           m_obj->tagname);
   1368     if (m_obj->map_handle != NULL) {
   1369         m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
   1370                                              dwDesiredAccess,
   1371                                              off_hi,
   1372                                              off_lo,
   1373                                              m_obj->size);
   1374         if (m_obj->data != NULL)
   1375             return (PyObject *)m_obj;
   1376         else {
   1377             dwErr = GetLastError();
   1378             CloseHandle(m_obj->map_handle);
   1379             m_obj->map_handle = NULL;
   1380         }
   1381     } else
   1382         dwErr = GetLastError();
   1383     Py_DECREF(m_obj);
   1384     PyErr_SetFromWindowsErr(dwErr);
   1385     return NULL;
   1386 }
   1387 #endif /* MS_WINDOWS */
   1388 
   1389 static void
   1390 setint(PyObject *d, const char *name, long value)
   1391 {
   1392     PyObject *o = PyLong_FromLong(value);
   1393     if (o && PyDict_SetItemString(d, name, o) == 0) {
   1394         Py_DECREF(o);
   1395     }
   1396 }
   1397 
   1398 
   1399 static struct PyModuleDef mmapmodule = {
   1400     PyModuleDef_HEAD_INIT,
   1401     "mmap",
   1402     NULL,
   1403     -1,
   1404     NULL,
   1405     NULL,
   1406     NULL,
   1407     NULL,
   1408     NULL
   1409 };
   1410 
   1411 PyMODINIT_FUNC
   1412 PyInit_mmap(void)
   1413 {
   1414     PyObject *dict, *module;
   1415 
   1416     if (PyType_Ready(&mmap_object_type) < 0)
   1417         return NULL;
   1418 
   1419     module = PyModule_Create(&mmapmodule);
   1420     if (module == NULL)
   1421         return NULL;
   1422     dict = PyModule_GetDict(module);
   1423     if (!dict)
   1424         return NULL;
   1425     PyDict_SetItemString(dict, "error", PyExc_OSError);
   1426     PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
   1427 #ifdef PROT_EXEC
   1428     setint(dict, "PROT_EXEC", PROT_EXEC);
   1429 #endif
   1430 #ifdef PROT_READ
   1431     setint(dict, "PROT_READ", PROT_READ);
   1432 #endif
   1433 #ifdef PROT_WRITE
   1434     setint(dict, "PROT_WRITE", PROT_WRITE);
   1435 #endif
   1436 
   1437 #ifdef MAP_SHARED
   1438     setint(dict, "MAP_SHARED", MAP_SHARED);
   1439 #endif
   1440 #ifdef MAP_PRIVATE
   1441     setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
   1442 #endif
   1443 #ifdef MAP_DENYWRITE
   1444     setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
   1445 #endif
   1446 #ifdef MAP_EXECUTABLE
   1447     setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
   1448 #endif
   1449 #ifdef MAP_ANONYMOUS
   1450     setint(dict, "MAP_ANON", MAP_ANONYMOUS);
   1451     setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
   1452 #endif
   1453 
   1454     setint(dict, "PAGESIZE", (long)my_getpagesize());
   1455 
   1456     setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
   1457 
   1458     setint(dict, "ACCESS_DEFAULT", ACCESS_DEFAULT);
   1459     setint(dict, "ACCESS_READ", ACCESS_READ);
   1460     setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
   1461     setint(dict, "ACCESS_COPY", ACCESS_COPY);
   1462     return module;
   1463 }
   1464