Python-2.7.3/Modules/mmapmodule.c

No issues found

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