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 }