Saved and restored logging._handlerList at the same time as saving/restoring logging...
[python.git] / Modules / mmapmodule.c
blobb6b2d85301ad05c3dbe17c6fbb5f597acef585c1
1 /*
2 / Author: Sam Rushing <rushing@nightmare.com>
3 / Hacked for Unix by AMK
4 / $Id$
6 / mmapmodule.cpp -- map a view of a file into memory
8 / todo: need permission flags, perhaps a 'chsize' analog
9 / not all functions check range yet!!!
12 / Note: This module currently only deals with 32-bit file
13 / sizes.
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.
21 #include <Python.h>
23 #ifndef MS_WINDOWS
24 #define UNIX
25 #endif
27 #ifdef MS_WINDOWS
28 #include <windows.h>
29 static int
30 my_getpagesize(void)
32 SYSTEM_INFO si;
33 GetSystemInfo(&si);
34 return si.dwPageSize;
36 #endif
38 #ifdef UNIX
39 #include <sys/mman.h>
40 #include <sys/stat.h>
42 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
43 static int
44 my_getpagesize(void)
46 return sysconf(_SC_PAGESIZE);
48 #else
49 #define my_getpagesize getpagesize
50 #endif
52 #endif /* UNIX */
54 #include <string.h>
55 #include <sys/types.h>
57 /* maybe define MAP_ANON in terms of MAP_ANONYMOUS */
58 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
59 # define MAP_ANONYMOUS MAP_ANON
60 #endif
62 static PyObject *mmap_module_error;
64 typedef enum
66 ACCESS_DEFAULT,
67 ACCESS_READ,
68 ACCESS_WRITE,
69 ACCESS_COPY
70 } access_mode;
72 typedef struct {
73 PyObject_HEAD
74 char * data;
75 size_t size;
76 size_t pos;
78 #ifdef MS_WINDOWS
79 HANDLE map_handle;
80 HANDLE file_handle;
81 char * tagname;
82 #endif
84 #ifdef UNIX
85 int fd;
86 #endif
88 access_mode access;
89 } mmap_object;
92 static void
93 mmap_object_dealloc(mmap_object *m_obj)
95 #ifdef MS_WINDOWS
96 if (m_obj->data != NULL)
97 UnmapViewOfFile (m_obj->data);
98 if (m_obj->map_handle != INVALID_HANDLE_VALUE)
99 CloseHandle (m_obj->map_handle);
100 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
101 CloseHandle (m_obj->file_handle);
102 if (m_obj->tagname)
103 PyMem_Free(m_obj->tagname);
104 #endif /* MS_WINDOWS */
106 #ifdef UNIX
107 if (m_obj->fd >= 0)
108 (void) close(m_obj->fd);
109 if (m_obj->data!=NULL) {
110 msync(m_obj->data, m_obj->size, MS_SYNC);
111 munmap(m_obj->data, m_obj->size);
113 #endif /* UNIX */
115 PyObject_Del(m_obj);
118 static PyObject *
119 mmap_close_method(mmap_object *self, PyObject *args)
121 if (!PyArg_ParseTuple(args, ":close"))
122 return NULL;
123 #ifdef MS_WINDOWS
124 /* For each resource we maintain, we need to check
125 the value is valid, and if so, free the resource
126 and set the member value to an invalid value so
127 the dealloc does not attempt to resource clearing
128 again.
129 TODO - should we check for errors in the close operations???
131 if (self->data != NULL) {
132 UnmapViewOfFile (self->data);
133 self->data = NULL;
135 if (self->map_handle != INVALID_HANDLE_VALUE) {
136 CloseHandle (self->map_handle);
137 self->map_handle = INVALID_HANDLE_VALUE;
139 if (self->file_handle != INVALID_HANDLE_VALUE) {
140 CloseHandle (self->file_handle);
141 self->file_handle = INVALID_HANDLE_VALUE;
143 #endif /* MS_WINDOWS */
145 #ifdef UNIX
146 (void) close(self->fd);
147 self->fd = -1;
148 if (self->data != NULL) {
149 munmap(self->data, self->size);
150 self->data = NULL;
152 #endif
154 Py_INCREF (Py_None);
155 return (Py_None);
158 #ifdef MS_WINDOWS
159 #define CHECK_VALID(err) \
160 do { \
161 if (self->map_handle == INVALID_HANDLE_VALUE) { \
162 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
163 return err; \
165 } while (0)
166 #endif /* MS_WINDOWS */
168 #ifdef UNIX
169 #define CHECK_VALID(err) \
170 do { \
171 if (self->data == NULL) { \
172 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
173 return err; \
175 } while (0)
176 #endif /* UNIX */
178 static PyObject *
179 mmap_read_byte_method(mmap_object *self,
180 PyObject *args)
182 CHECK_VALID(NULL);
183 if (!PyArg_ParseTuple(args, ":read_byte"))
184 return NULL;
185 if (self->pos < self->size) {
186 char value = self->data[self->pos];
187 self->pos += 1;
188 return Py_BuildValue("c", value);
189 } else {
190 PyErr_SetString (PyExc_ValueError, "read byte out of range");
191 return NULL;
195 static PyObject *
196 mmap_read_line_method(mmap_object *self,
197 PyObject *args)
199 char *start = self->data+self->pos;
200 char *eof = self->data+self->size;
201 char *eol;
202 PyObject *result;
204 CHECK_VALID(NULL);
205 if (!PyArg_ParseTuple(args, ":readline"))
206 return NULL;
208 eol = memchr(start, '\n', self->size - self->pos);
209 if (!eol)
210 eol = eof;
211 else
212 ++eol; /* we're interested in the position after the
213 newline. */
214 result = PyString_FromStringAndSize(start, (eol - start));
215 self->pos += (eol - start);
216 return (result);
219 static PyObject *
220 mmap_read_method(mmap_object *self,
221 PyObject *args)
223 long num_bytes;
224 PyObject *result;
226 CHECK_VALID(NULL);
227 if (!PyArg_ParseTuple(args, "l:read", &num_bytes))
228 return(NULL);
230 /* silently 'adjust' out-of-range requests */
231 if ((self->pos + num_bytes) > self->size) {
232 num_bytes -= (self->pos+num_bytes) - self->size;
234 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
235 self->pos += num_bytes;
236 return (result);
239 static PyObject *
240 mmap_find_method(mmap_object *self,
241 PyObject *args)
243 long start = self->pos;
244 char *needle;
245 int len;
247 CHECK_VALID(NULL);
248 if (!PyArg_ParseTuple (args, "s#|l:find", &needle, &len, &start)) {
249 return NULL;
250 } else {
251 char *p;
252 char *e = self->data + self->size;
254 if (start < 0)
255 start += self->size;
256 if (start < 0)
257 start = 0;
258 else if ((size_t)start > self->size)
259 start = self->size;
261 for (p = self->data + start; p + len <= e; ++p) {
262 int i;
263 for (i = 0; i < len && needle[i] == p[i]; ++i)
264 /* nothing */;
265 if (i == len) {
266 return Py_BuildValue (
267 "l",
268 (long) (p - self->data));
271 return Py_BuildValue ("l", (long) -1);
275 static int
276 is_writeable(mmap_object *self)
278 if (self->access != ACCESS_READ)
279 return 1;
280 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
281 return 0;
284 static int
285 is_resizeable(mmap_object *self)
287 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
288 return 1;
289 PyErr_Format(PyExc_TypeError,
290 "mmap can't resize a readonly or copy-on-write memory map.");
291 return 0;
295 static PyObject *
296 mmap_write_method(mmap_object *self,
297 PyObject *args)
299 int length;
300 char *data;
302 CHECK_VALID(NULL);
303 if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
304 return(NULL);
306 if (!is_writeable(self))
307 return NULL;
309 if ((self->pos + length) > self->size) {
310 PyErr_SetString (PyExc_ValueError, "data out of range");
311 return NULL;
313 memcpy (self->data+self->pos, data, length);
314 self->pos = self->pos+length;
315 Py_INCREF (Py_None);
316 return (Py_None);
319 static PyObject *
320 mmap_write_byte_method(mmap_object *self,
321 PyObject *args)
323 char value;
325 CHECK_VALID(NULL);
326 if (!PyArg_ParseTuple (args, "c:write_byte", &value))
327 return(NULL);
329 if (!is_writeable(self))
330 return NULL;
331 *(self->data+self->pos) = value;
332 self->pos += 1;
333 Py_INCREF (Py_None);
334 return (Py_None);
337 static PyObject *
338 mmap_size_method(mmap_object *self,
339 PyObject *args)
341 CHECK_VALID(NULL);
342 if (!PyArg_ParseTuple(args, ":size"))
343 return NULL;
345 #ifdef MS_WINDOWS
346 if (self->file_handle != INVALID_HANDLE_VALUE) {
347 return (Py_BuildValue (
348 "l", (long)
349 GetFileSize (self->file_handle, NULL)));
350 } else {
351 return (Py_BuildValue ("l", (long) self->size) );
353 #endif /* MS_WINDOWS */
355 #ifdef UNIX
357 struct stat buf;
358 if (-1 == fstat(self->fd, &buf)) {
359 PyErr_SetFromErrno(mmap_module_error);
360 return NULL;
362 return (Py_BuildValue ("l", (long) buf.st_size) );
364 #endif /* UNIX */
367 /* This assumes that you want the entire file mapped,
368 / and when recreating the map will make the new file
369 / have the new size
371 / Is this really necessary? This could easily be done
372 / from python by just closing and re-opening with the
373 / new size?
376 static PyObject *
377 mmap_resize_method(mmap_object *self,
378 PyObject *args)
380 unsigned long new_size;
381 CHECK_VALID(NULL);
382 if (!PyArg_ParseTuple (args, "k:resize", &new_size) ||
383 !is_resizeable(self)) {
384 return NULL;
385 #ifdef MS_WINDOWS
386 } else {
387 DWORD dwErrCode = 0;
388 /* First, unmap the file view */
389 UnmapViewOfFile (self->data);
390 /* Close the mapping object */
391 CloseHandle (self->map_handle);
392 /* Move to the desired EOF position */
393 SetFilePointer (self->file_handle,
394 new_size, NULL, FILE_BEGIN);
395 /* Change the size of the file */
396 SetEndOfFile (self->file_handle);
397 /* Create another mapping object and remap the file view */
398 self->map_handle = CreateFileMapping (
399 self->file_handle,
400 NULL,
401 PAGE_READWRITE,
403 new_size,
404 self->tagname);
405 if (self->map_handle != NULL) {
406 self->data = (char *) MapViewOfFile (self->map_handle,
407 FILE_MAP_WRITE,
411 if (self->data != NULL) {
412 self->size = new_size;
413 Py_INCREF (Py_None);
414 return Py_None;
415 } else {
416 dwErrCode = GetLastError();
418 } else {
419 dwErrCode = GetLastError();
421 PyErr_SetFromWindowsErr(dwErrCode);
422 return (NULL);
423 #endif /* MS_WINDOWS */
425 #ifdef UNIX
426 #ifndef HAVE_MREMAP
427 } else {
428 PyErr_SetString(PyExc_SystemError,
429 "mmap: resizing not available--no mremap()");
430 return NULL;
431 #else
432 } else {
433 void *newmap;
435 if (ftruncate(self->fd, new_size) == -1) {
436 PyErr_SetFromErrno(mmap_module_error);
437 return NULL;
440 #ifdef MREMAP_MAYMOVE
441 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
442 #else
443 newmap = mremap(self->data, self->size, new_size, 0);
444 #endif
445 if (newmap == (void *)-1)
447 PyErr_SetFromErrno(mmap_module_error);
448 return NULL;
450 self->data = newmap;
451 self->size = new_size;
452 Py_INCREF(Py_None);
453 return Py_None;
454 #endif /* HAVE_MREMAP */
455 #endif /* UNIX */
459 static PyObject *
460 mmap_tell_method(mmap_object *self, PyObject *args)
462 CHECK_VALID(NULL);
463 if (!PyArg_ParseTuple(args, ":tell"))
464 return NULL;
465 return (Py_BuildValue ("l", (long) self->pos) );
468 static PyObject *
469 mmap_flush_method(mmap_object *self, PyObject *args)
471 unsigned long offset = 0;
472 unsigned long size = self->size;
473 CHECK_VALID(NULL);
474 if (!PyArg_ParseTuple (args, "|kk:flush", &offset, &size)) {
475 return NULL;
476 } else if ((offset + size) > self->size) {
477 PyErr_SetString (PyExc_ValueError,
478 "flush values out of range");
479 return NULL;
480 } else {
481 #ifdef MS_WINDOWS
482 return (Py_BuildValue("l", (long)
483 FlushViewOfFile(self->data+offset, size)));
484 #endif /* MS_WINDOWS */
485 #ifdef UNIX
486 /* XXX semantics of return value? */
487 /* XXX flags for msync? */
488 if (-1 == msync(self->data + offset, size,
489 MS_SYNC))
491 PyErr_SetFromErrno(mmap_module_error);
492 return NULL;
494 return Py_BuildValue ("l", (long) 0);
495 #endif /* UNIX */
499 static PyObject *
500 mmap_seek_method(mmap_object *self, PyObject *args)
502 int dist;
503 int how=0;
504 CHECK_VALID(NULL);
505 if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) {
506 return(NULL);
507 } else {
508 size_t where;
509 switch (how) {
510 case 0: /* relative to start */
511 if (dist < 0)
512 goto onoutofrange;
513 where = dist;
514 break;
515 case 1: /* relative to current position */
516 if ((int)self->pos + dist < 0)
517 goto onoutofrange;
518 where = self->pos + dist;
519 break;
520 case 2: /* relative to end */
521 if ((int)self->size + dist < 0)
522 goto onoutofrange;
523 where = self->size + dist;
524 break;
525 default:
526 PyErr_SetString (PyExc_ValueError,
527 "unknown seek type");
528 return NULL;
530 if (where > self->size)
531 goto onoutofrange;
532 self->pos = where;
533 Py_INCREF (Py_None);
534 return (Py_None);
537 onoutofrange:
538 PyErr_SetString (PyExc_ValueError, "seek out of range");
539 return NULL;
542 static PyObject *
543 mmap_move_method(mmap_object *self, PyObject *args)
545 unsigned long dest, src, count;
546 CHECK_VALID(NULL);
547 if (!PyArg_ParseTuple (args, "kkk:move", &dest, &src, &count) ||
548 !is_writeable(self)) {
549 return NULL;
550 } else {
551 /* bounds check the values */
552 if (/* end of source after end of data?? */
553 ((src+count) > self->size)
554 /* dest will fit? */
555 || (dest+count > self->size)) {
556 PyErr_SetString (PyExc_ValueError,
557 "source or destination out of range");
558 return NULL;
559 } else {
560 memmove (self->data+dest, self->data+src, count);
561 Py_INCREF (Py_None);
562 return Py_None;
567 static struct PyMethodDef mmap_object_methods[] = {
568 {"close", (PyCFunction) mmap_close_method, METH_VARARGS},
569 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
570 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
571 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
572 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
573 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_VARARGS},
574 {"readline", (PyCFunction) mmap_read_line_method, METH_VARARGS},
575 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
576 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
577 {"size", (PyCFunction) mmap_size_method, METH_VARARGS},
578 {"tell", (PyCFunction) mmap_tell_method, METH_VARARGS},
579 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
580 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
581 {NULL, NULL} /* sentinel */
584 /* Functions for treating an mmap'ed file as a buffer */
586 static int
587 mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr)
589 CHECK_VALID(-1);
590 if ( index != 0 ) {
591 PyErr_SetString(PyExc_SystemError,
592 "Accessing non-existent mmap segment");
593 return -1;
595 *ptr = self->data;
596 return self->size;
599 static int
600 mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
602 CHECK_VALID(-1);
603 if ( index != 0 ) {
604 PyErr_SetString(PyExc_SystemError,
605 "Accessing non-existent mmap segment");
606 return -1;
608 if (!is_writeable(self))
609 return -1;
610 *ptr = self->data;
611 return self->size;
614 static int
615 mmap_buffer_getsegcount(mmap_object *self, int *lenp)
617 CHECK_VALID(-1);
618 if (lenp)
619 *lenp = self->size;
620 return 1;
623 static int
624 mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr)
626 if ( index != 0 ) {
627 PyErr_SetString(PyExc_SystemError,
628 "accessing non-existent buffer segment");
629 return -1;
631 *ptr = (const char *)self->data;
632 return self->size;
635 static PyObject *
636 mmap_object_getattr(mmap_object *self, char *name)
638 return Py_FindMethod (mmap_object_methods, (PyObject *)self, name);
641 static int
642 mmap_length(mmap_object *self)
644 CHECK_VALID(-1);
645 return self->size;
648 static PyObject *
649 mmap_item(mmap_object *self, int i)
651 CHECK_VALID(NULL);
652 if (i < 0 || (size_t)i >= self->size) {
653 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
654 return NULL;
656 return PyString_FromStringAndSize(self->data + i, 1);
659 static PyObject *
660 mmap_slice(mmap_object *self, int ilow, int ihigh)
662 CHECK_VALID(NULL);
663 if (ilow < 0)
664 ilow = 0;
665 else if ((size_t)ilow > self->size)
666 ilow = self->size;
667 if (ihigh < 0)
668 ihigh = 0;
669 if (ihigh < ilow)
670 ihigh = ilow;
671 else if ((size_t)ihigh > self->size)
672 ihigh = self->size;
674 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
677 static PyObject *
678 mmap_concat(mmap_object *self, PyObject *bb)
680 CHECK_VALID(NULL);
681 PyErr_SetString(PyExc_SystemError,
682 "mmaps don't support concatenation");
683 return NULL;
686 static PyObject *
687 mmap_repeat(mmap_object *self, int n)
689 CHECK_VALID(NULL);
690 PyErr_SetString(PyExc_SystemError,
691 "mmaps don't support repeat operation");
692 return NULL;
695 static int
696 mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
698 const char *buf;
700 CHECK_VALID(-1);
701 if (ilow < 0)
702 ilow = 0;
703 else if ((size_t)ilow > self->size)
704 ilow = self->size;
705 if (ihigh < 0)
706 ihigh = 0;
707 if (ihigh < ilow)
708 ihigh = ilow;
709 else if ((size_t)ihigh > self->size)
710 ihigh = self->size;
712 if (v == NULL) {
713 PyErr_SetString(PyExc_TypeError,
714 "mmap object doesn't support slice deletion");
715 return -1;
717 if (! (PyString_Check(v)) ) {
718 PyErr_SetString(PyExc_IndexError,
719 "mmap slice assignment must be a string");
720 return -1;
722 if ( PyString_Size(v) != (ihigh - ilow) ) {
723 PyErr_SetString(PyExc_IndexError,
724 "mmap slice assignment is wrong size");
725 return -1;
727 if (!is_writeable(self))
728 return -1;
729 buf = PyString_AsString(v);
730 memcpy(self->data + ilow, buf, ihigh-ilow);
731 return 0;
734 static int
735 mmap_ass_item(mmap_object *self, int i, PyObject *v)
737 const char *buf;
739 CHECK_VALID(-1);
740 if (i < 0 || (size_t)i >= self->size) {
741 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
742 return -1;
744 if (v == NULL) {
745 PyErr_SetString(PyExc_TypeError,
746 "mmap object doesn't support item deletion");
747 return -1;
749 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
750 PyErr_SetString(PyExc_IndexError,
751 "mmap assignment must be single-character string");
752 return -1;
754 if (!is_writeable(self))
755 return -1;
756 buf = PyString_AsString(v);
757 self->data[i] = buf[0];
758 return 0;
761 static PySequenceMethods mmap_as_sequence = {
762 (inquiry)mmap_length, /*sq_length*/
763 (binaryfunc)mmap_concat, /*sq_concat*/
764 (intargfunc)mmap_repeat, /*sq_repeat*/
765 (intargfunc)mmap_item, /*sq_item*/
766 (intintargfunc)mmap_slice, /*sq_slice*/
767 (intobjargproc)mmap_ass_item, /*sq_ass_item*/
768 (intintobjargproc)mmap_ass_slice, /*sq_ass_slice*/
771 static PyBufferProcs mmap_as_buffer = {
772 (getreadbufferproc)mmap_buffer_getreadbuf,
773 (getwritebufferproc)mmap_buffer_getwritebuf,
774 (getsegcountproc)mmap_buffer_getsegcount,
775 (getcharbufferproc)mmap_buffer_getcharbuffer,
778 static PyTypeObject mmap_object_type = {
779 PyObject_HEAD_INIT(0) /* patched in module init */
780 0, /* ob_size */
781 "mmap.mmap", /* tp_name */
782 sizeof(mmap_object), /* tp_size */
783 0, /* tp_itemsize */
784 /* methods */
785 (destructor) mmap_object_dealloc, /* tp_dealloc */
786 0, /* tp_print */
787 (getattrfunc) mmap_object_getattr, /* tp_getattr */
788 0, /* tp_setattr */
789 0, /* tp_compare */
790 0, /* tp_repr */
791 0, /* tp_as_number */
792 &mmap_as_sequence, /*tp_as_sequence*/
793 0, /*tp_as_mapping*/
794 0, /*tp_hash*/
795 0, /*tp_call*/
796 0, /*tp_str*/
797 0, /*tp_getattro*/
798 0, /*tp_setattro*/
799 &mmap_as_buffer, /*tp_as_buffer*/
800 Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
801 0, /*tp_doc*/
805 /* extract the map size from the given PyObject
807 The map size is restricted to [0, INT_MAX] because this is the current
808 Python limitation on object sizes. Although the mmap object *could* handle
809 a larger map size, there is no point because all the useful operations
810 (len(), slicing(), sequence indexing) are limited by a C int.
812 Returns -1 on error, with an appropriate Python exception raised. On
813 success, the map size is returned. */
814 static int
815 _GetMapSize(PyObject *o)
817 if (PyInt_Check(o)) {
818 long i = PyInt_AsLong(o);
819 if (PyErr_Occurred())
820 return -1;
821 if (i < 0)
822 goto onnegoverflow;
823 if (i > INT_MAX)
824 goto onposoverflow;
825 return (int)i;
827 else if (PyLong_Check(o)) {
828 long i = PyLong_AsLong(o);
829 if (PyErr_Occurred()) {
830 /* yes negative overflow is mistaken for positive overflow
831 but not worth the trouble to check sign of 'i' */
832 if (PyErr_ExceptionMatches(PyExc_OverflowError))
833 goto onposoverflow;
834 else
835 return -1;
837 if (i < 0)
838 goto onnegoverflow;
839 if (i > INT_MAX)
840 goto onposoverflow;
841 return (int)i;
843 else {
844 PyErr_SetString(PyExc_TypeError,
845 "map size must be an integral value");
846 return -1;
849 onnegoverflow:
850 PyErr_SetString(PyExc_OverflowError,
851 "memory mapped size must be positive");
852 return -1;
854 onposoverflow:
855 PyErr_SetString(PyExc_OverflowError,
856 "memory mapped size is too large (limited by C int)");
857 return -1;
860 #ifdef UNIX
861 static PyObject *
862 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
864 #ifdef HAVE_FSTAT
865 struct stat st;
866 #endif
867 mmap_object *m_obj;
868 PyObject *map_size_obj = NULL;
869 int map_size;
870 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
871 int devzero = -1;
872 int access = (int)ACCESS_DEFAULT;
873 static const char *keywords[] = {"fileno", "length",
874 "flags", "prot",
875 "access", NULL};
877 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords,
878 &fd, &map_size_obj, &flags, &prot,
879 &access))
880 return NULL;
881 map_size = _GetMapSize(map_size_obj);
882 if (map_size < 0)
883 return NULL;
885 if ((access != (int)ACCESS_DEFAULT) &&
886 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
887 return PyErr_Format(PyExc_ValueError,
888 "mmap can't specify both access and flags, prot.");
889 switch ((access_mode)access) {
890 case ACCESS_READ:
891 flags = MAP_SHARED;
892 prot = PROT_READ;
893 break;
894 case ACCESS_WRITE:
895 flags = MAP_SHARED;
896 prot = PROT_READ | PROT_WRITE;
897 break;
898 case ACCESS_COPY:
899 flags = MAP_PRIVATE;
900 prot = PROT_READ | PROT_WRITE;
901 break;
902 case ACCESS_DEFAULT:
903 /* use the specified or default values of flags and prot */
904 break;
905 default:
906 return PyErr_Format(PyExc_ValueError,
907 "mmap invalid access parameter.");
910 #ifdef HAVE_FSTAT
911 # ifdef __VMS
912 /* on OpenVMS we must ensure that all bytes are written to the file */
913 fsync(fd);
914 # endif
915 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
916 if (map_size == 0) {
917 map_size = (int)st.st_size;
918 } else if ((size_t)map_size > st.st_size) {
919 PyErr_SetString(PyExc_ValueError,
920 "mmap length is greater than file size");
921 return NULL;
924 #endif
925 m_obj = PyObject_New (mmap_object, &mmap_object_type);
926 if (m_obj == NULL) {return NULL;}
927 m_obj->data = NULL;
928 m_obj->size = (size_t) map_size;
929 m_obj->pos = (size_t) 0;
930 if (fd == -1) {
931 m_obj->fd = -1;
932 /* Assume the caller wants to map anonymous memory.
933 This is the same behaviour as Windows. mmap.mmap(-1, size)
934 on both Windows and Unix map anonymous memory.
936 #ifdef MAP_ANONYMOUS
937 /* BSD way to map anonymous memory */
938 flags |= MAP_ANONYMOUS;
939 #else
940 /* SVR4 method to map anonymous memory is to open /dev/zero */
941 fd = devzero = open("/dev/zero", O_RDWR);
942 if (devzero == -1) {
943 Py_DECREF(m_obj);
944 PyErr_SetFromErrno(mmap_module_error);
945 return NULL;
947 #endif
948 } else {
949 m_obj->fd = dup(fd);
950 if (m_obj->fd == -1) {
951 Py_DECREF(m_obj);
952 PyErr_SetFromErrno(mmap_module_error);
953 return NULL;
957 m_obj->data = mmap(NULL, map_size,
958 prot, flags,
959 fd, 0);
961 if (devzero != -1) {
962 close(devzero);
965 if (m_obj->data == (char *)-1) {
966 m_obj->data = NULL;
967 Py_DECREF(m_obj);
968 PyErr_SetFromErrno(mmap_module_error);
969 return NULL;
971 m_obj->access = (access_mode)access;
972 return (PyObject *)m_obj;
974 #endif /* UNIX */
976 #ifdef MS_WINDOWS
977 static PyObject *
978 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
980 mmap_object *m_obj;
981 PyObject *map_size_obj = NULL;
982 int map_size;
983 char *tagname = "";
984 DWORD dwErr = 0;
985 int fileno;
986 HANDLE fh = 0;
987 int access = (access_mode)ACCESS_DEFAULT;
988 DWORD flProtect, dwDesiredAccess;
989 static const char *keywords[] = { "fileno", "length",
990 "tagname",
991 "access", NULL };
993 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
994 &fileno, &map_size_obj,
995 &tagname, &access)) {
996 return NULL;
999 switch((access_mode)access) {
1000 case ACCESS_READ:
1001 flProtect = PAGE_READONLY;
1002 dwDesiredAccess = FILE_MAP_READ;
1003 break;
1004 case ACCESS_DEFAULT: case ACCESS_WRITE:
1005 flProtect = PAGE_READWRITE;
1006 dwDesiredAccess = FILE_MAP_WRITE;
1007 break;
1008 case ACCESS_COPY:
1009 flProtect = PAGE_WRITECOPY;
1010 dwDesiredAccess = FILE_MAP_COPY;
1011 break;
1012 default:
1013 return PyErr_Format(PyExc_ValueError,
1014 "mmap invalid access parameter.");
1017 map_size = _GetMapSize(map_size_obj);
1018 if (map_size < 0)
1019 return NULL;
1021 /* assume -1 and 0 both mean invalid filedescriptor
1022 to 'anonymously' map memory.
1023 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1024 XXX: Should this code be added?
1025 if (fileno == 0)
1026 PyErr_Warn(PyExc_DeprecationWarning,
1027 "don't use 0 for anonymous memory");
1029 if (fileno != -1 && fileno != 0) {
1030 fh = (HANDLE)_get_osfhandle(fileno);
1031 if (fh==(HANDLE)-1) {
1032 PyErr_SetFromErrno(mmap_module_error);
1033 return NULL;
1035 /* Win9x appears to need us seeked to zero */
1036 lseek(fileno, 0, SEEK_SET);
1039 m_obj = PyObject_New (mmap_object, &mmap_object_type);
1040 if (m_obj==NULL)
1041 return NULL;
1042 /* Set every field to an invalid marker, so we can safely
1043 destruct the object in the face of failure */
1044 m_obj->data = NULL;
1045 m_obj->file_handle = INVALID_HANDLE_VALUE;
1046 m_obj->map_handle = INVALID_HANDLE_VALUE;
1047 m_obj->tagname = NULL;
1049 if (fh) {
1050 /* It is necessary to duplicate the handle, so the
1051 Python code can close it on us */
1052 if (!DuplicateHandle(
1053 GetCurrentProcess(), /* source process handle */
1054 fh, /* handle to be duplicated */
1055 GetCurrentProcess(), /* target proc handle */
1056 (LPHANDLE)&m_obj->file_handle, /* result */
1057 0, /* access - ignored due to options value */
1058 FALSE, /* inherited by child processes? */
1059 DUPLICATE_SAME_ACCESS)) { /* options */
1060 dwErr = GetLastError();
1061 Py_DECREF(m_obj);
1062 PyErr_SetFromWindowsErr(dwErr);
1063 return NULL;
1065 if (!map_size) {
1066 m_obj->size = GetFileSize (fh, NULL);
1067 } else {
1068 m_obj->size = map_size;
1071 else {
1072 m_obj->size = map_size;
1075 /* set the initial position */
1076 m_obj->pos = (size_t) 0;
1078 /* set the tag name */
1079 if (tagname != NULL && *tagname != '\0') {
1080 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1081 if (m_obj->tagname == NULL) {
1082 PyErr_NoMemory();
1083 Py_DECREF(m_obj);
1084 return NULL;
1086 strcpy(m_obj->tagname, tagname);
1088 else
1089 m_obj->tagname = NULL;
1091 m_obj->access = (access_mode)access;
1092 m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
1093 NULL,
1094 flProtect,
1096 m_obj->size,
1097 m_obj->tagname);
1098 if (m_obj->map_handle != NULL) {
1099 m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
1100 dwDesiredAccess,
1104 if (m_obj->data != NULL) {
1105 return ((PyObject *) m_obj);
1106 } else {
1107 dwErr = GetLastError();
1109 } else {
1110 dwErr = GetLastError();
1112 Py_DECREF(m_obj);
1113 PyErr_SetFromWindowsErr(dwErr);
1114 return (NULL);
1116 #endif /* MS_WINDOWS */
1118 /* List of functions exported by this module */
1119 static struct PyMethodDef mmap_functions[] = {
1120 {"mmap", (PyCFunction) new_mmap_object,
1121 METH_VARARGS|METH_KEYWORDS},
1122 {NULL, NULL} /* Sentinel */
1125 PyMODINIT_FUNC
1126 initmmap(void)
1128 PyObject *dict, *module;
1130 /* Patch the object type */
1131 mmap_object_type.ob_type = &PyType_Type;
1133 module = Py_InitModule ("mmap", mmap_functions);
1134 if (module == NULL)
1135 return;
1136 dict = PyModule_GetDict (module);
1137 mmap_module_error = PyExc_EnvironmentError;
1138 Py_INCREF(mmap_module_error);
1139 PyDict_SetItemString (dict, "error", mmap_module_error);
1140 #ifdef PROT_EXEC
1141 PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) );
1142 #endif
1143 #ifdef PROT_READ
1144 PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) );
1145 #endif
1146 #ifdef PROT_WRITE
1147 PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) );
1148 #endif
1150 #ifdef MAP_SHARED
1151 PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) );
1152 #endif
1153 #ifdef MAP_PRIVATE
1154 PyDict_SetItemString (dict, "MAP_PRIVATE",
1155 PyInt_FromLong(MAP_PRIVATE) );
1156 #endif
1157 #ifdef MAP_DENYWRITE
1158 PyDict_SetItemString (dict, "MAP_DENYWRITE",
1159 PyInt_FromLong(MAP_DENYWRITE) );
1160 #endif
1161 #ifdef MAP_EXECUTABLE
1162 PyDict_SetItemString (dict, "MAP_EXECUTABLE",
1163 PyInt_FromLong(MAP_EXECUTABLE) );
1164 #endif
1165 #ifdef MAP_ANONYMOUS
1166 PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANONYMOUS) );
1167 PyDict_SetItemString (dict, "MAP_ANONYMOUS",
1168 PyInt_FromLong(MAP_ANONYMOUS) );
1169 #endif
1171 PyDict_SetItemString (dict, "PAGESIZE",
1172 PyInt_FromLong( (long)my_getpagesize() ) );
1174 PyDict_SetItemString (dict, "ACCESS_READ",
1175 PyInt_FromLong(ACCESS_READ));
1176 PyDict_SetItemString (dict, "ACCESS_WRITE",
1177 PyInt_FromLong(ACCESS_WRITE));
1178 PyDict_SetItemString (dict, "ACCESS_COPY",
1179 PyInt_FromLong(ACCESS_COPY));