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