Fixed bug in time-to-midnight calculation.
[python.git] / Modules / mmapmodule.c
blob2ff4494304198c9b64e881964a340074d2bac9bf
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 static PyObject *mmap_module_error;
59 typedef enum
61 ACCESS_DEFAULT,
62 ACCESS_READ,
63 ACCESS_WRITE,
64 ACCESS_COPY
65 } access_mode;
67 typedef struct {
68 PyObject_HEAD
69 char * data;
70 size_t size;
71 size_t pos;
73 #ifdef MS_WINDOWS
74 HANDLE map_handle;
75 HANDLE file_handle;
76 char * tagname;
77 #endif
79 #ifdef UNIX
80 int fd;
81 #endif
83 access_mode access;
84 } mmap_object;
87 static void
88 mmap_object_dealloc(mmap_object *m_obj)
90 #ifdef MS_WINDOWS
91 if (m_obj->data != NULL)
92 UnmapViewOfFile (m_obj->data);
93 if (m_obj->map_handle != INVALID_HANDLE_VALUE)
94 CloseHandle (m_obj->map_handle);
95 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
96 CloseHandle (m_obj->file_handle);
97 if (m_obj->tagname)
98 PyMem_Free(m_obj->tagname);
99 #endif /* MS_WINDOWS */
101 #ifdef UNIX
102 if (m_obj->fd >= 0)
103 (void) close(m_obj->fd);
104 if (m_obj->data!=NULL) {
105 msync(m_obj->data, m_obj->size, MS_SYNC);
106 munmap(m_obj->data, m_obj->size);
108 #endif /* UNIX */
110 PyObject_Del(m_obj);
113 static PyObject *
114 mmap_close_method(mmap_object *self, PyObject *args)
116 if (!PyArg_ParseTuple(args, ":close"))
117 return NULL;
118 #ifdef MS_WINDOWS
119 /* For each resource we maintain, we need to check
120 the value is valid, and if so, free the resource
121 and set the member value to an invalid value so
122 the dealloc does not attempt to resource clearing
123 again.
124 TODO - should we check for errors in the close operations???
126 if (self->data != NULL) {
127 UnmapViewOfFile (self->data);
128 self->data = NULL;
130 if (self->map_handle != INVALID_HANDLE_VALUE) {
131 CloseHandle (self->map_handle);
132 self->map_handle = INVALID_HANDLE_VALUE;
134 if (self->file_handle != INVALID_HANDLE_VALUE) {
135 CloseHandle (self->file_handle);
136 self->file_handle = INVALID_HANDLE_VALUE;
138 #endif /* MS_WINDOWS */
140 #ifdef UNIX
141 (void) close(self->fd);
142 self->fd = -1;
143 if (self->data != NULL) {
144 munmap(self->data, self->size);
145 self->data = NULL;
147 #endif
149 Py_INCREF (Py_None);
150 return (Py_None);
153 #ifdef MS_WINDOWS
154 #define CHECK_VALID(err) \
155 do { \
156 if (self->map_handle == INVALID_HANDLE_VALUE) { \
157 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
158 return err; \
160 } while (0)
161 #endif /* MS_WINDOWS */
163 #ifdef UNIX
164 #define CHECK_VALID(err) \
165 do { \
166 if (self->data == NULL) { \
167 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
168 return err; \
170 } while (0)
171 #endif /* UNIX */
173 static PyObject *
174 mmap_read_byte_method(mmap_object *self,
175 PyObject *args)
177 CHECK_VALID(NULL);
178 if (!PyArg_ParseTuple(args, ":read_byte"))
179 return NULL;
180 if (self->pos < self->size) {
181 char value = self->data[self->pos];
182 self->pos += 1;
183 return Py_BuildValue("c", value);
184 } else {
185 PyErr_SetString (PyExc_ValueError, "read byte out of range");
186 return NULL;
190 static PyObject *
191 mmap_read_line_method(mmap_object *self,
192 PyObject *args)
194 char *start = self->data+self->pos;
195 char *eof = self->data+self->size;
196 char *eol;
197 PyObject *result;
199 CHECK_VALID(NULL);
200 if (!PyArg_ParseTuple(args, ":readline"))
201 return NULL;
203 eol = memchr(start, '\n', self->size - self->pos);
204 if (!eol)
205 eol = eof;
206 else
207 ++eol; /* we're interested in the position after the
208 newline. */
209 result = PyString_FromStringAndSize(start, (eol - start));
210 self->pos += (eol - start);
211 return (result);
214 static PyObject *
215 mmap_read_method(mmap_object *self,
216 PyObject *args)
218 long num_bytes;
219 PyObject *result;
221 CHECK_VALID(NULL);
222 if (!PyArg_ParseTuple(args, "l:read", &num_bytes))
223 return(NULL);
225 /* silently 'adjust' out-of-range requests */
226 if ((self->pos + num_bytes) > self->size) {
227 num_bytes -= (self->pos+num_bytes) - self->size;
229 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
230 self->pos += num_bytes;
231 return (result);
234 static PyObject *
235 mmap_find_method(mmap_object *self,
236 PyObject *args)
238 long start = self->pos;
239 char *needle;
240 int len;
242 CHECK_VALID(NULL);
243 if (!PyArg_ParseTuple (args, "s#|l:find", &needle, &len, &start)) {
244 return NULL;
245 } else {
246 char *p;
247 char *e = self->data + self->size;
249 if (start < 0)
250 start += self->size;
251 if (start < 0)
252 start = 0;
253 else if ((size_t)start > self->size)
254 start = self->size;
256 for (p = self->data + start; p + len <= e; ++p) {
257 int i;
258 for (i = 0; i < len && needle[i] == p[i]; ++i)
259 /* nothing */;
260 if (i == len) {
261 return Py_BuildValue (
262 "l",
263 (long) (p - self->data));
266 return Py_BuildValue ("l", (long) -1);
270 static int
271 is_writeable(mmap_object *self)
273 if (self->access != ACCESS_READ)
274 return 1;
275 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
276 return 0;
279 static int
280 is_resizeable(mmap_object *self)
282 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
283 return 1;
284 PyErr_Format(PyExc_TypeError,
285 "mmap can't resize a readonly or copy-on-write memory map.");
286 return 0;
290 static PyObject *
291 mmap_write_method(mmap_object *self,
292 PyObject *args)
294 int length;
295 char *data;
297 CHECK_VALID(NULL);
298 if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
299 return(NULL);
301 if (!is_writeable(self))
302 return NULL;
304 if ((self->pos + length) > self->size) {
305 PyErr_SetString (PyExc_ValueError, "data out of range");
306 return NULL;
308 memcpy (self->data+self->pos, data, length);
309 self->pos = self->pos+length;
310 Py_INCREF (Py_None);
311 return (Py_None);
314 static PyObject *
315 mmap_write_byte_method(mmap_object *self,
316 PyObject *args)
318 char value;
320 CHECK_VALID(NULL);
321 if (!PyArg_ParseTuple (args, "c:write_byte", &value))
322 return(NULL);
324 if (!is_writeable(self))
325 return NULL;
326 *(self->data+self->pos) = value;
327 self->pos += 1;
328 Py_INCREF (Py_None);
329 return (Py_None);
332 static PyObject *
333 mmap_size_method(mmap_object *self,
334 PyObject *args)
336 CHECK_VALID(NULL);
337 if (!PyArg_ParseTuple(args, ":size"))
338 return NULL;
340 #ifdef MS_WINDOWS
341 if (self->file_handle != INVALID_HANDLE_VALUE) {
342 return (Py_BuildValue (
343 "l", (long)
344 GetFileSize (self->file_handle, NULL)));
345 } else {
346 return (Py_BuildValue ("l", (long) self->size) );
348 #endif /* MS_WINDOWS */
350 #ifdef UNIX
352 struct stat buf;
353 if (-1 == fstat(self->fd, &buf)) {
354 PyErr_SetFromErrno(mmap_module_error);
355 return NULL;
357 return (Py_BuildValue ("l", (long) buf.st_size) );
359 #endif /* UNIX */
362 /* This assumes that you want the entire file mapped,
363 / and when recreating the map will make the new file
364 / have the new size
366 / Is this really necessary? This could easily be done
367 / from python by just closing and re-opening with the
368 / new size?
371 static PyObject *
372 mmap_resize_method(mmap_object *self,
373 PyObject *args)
375 unsigned long new_size;
376 CHECK_VALID(NULL);
377 if (!PyArg_ParseTuple (args, "k:resize", &new_size) ||
378 !is_resizeable(self)) {
379 return NULL;
380 #ifdef MS_WINDOWS
381 } else {
382 DWORD dwErrCode = 0;
383 /* First, unmap the file view */
384 UnmapViewOfFile (self->data);
385 /* Close the mapping object */
386 CloseHandle (self->map_handle);
387 /* Move to the desired EOF position */
388 SetFilePointer (self->file_handle,
389 new_size, NULL, FILE_BEGIN);
390 /* Change the size of the file */
391 SetEndOfFile (self->file_handle);
392 /* Create another mapping object and remap the file view */
393 self->map_handle = CreateFileMapping (
394 self->file_handle,
395 NULL,
396 PAGE_READWRITE,
398 new_size,
399 self->tagname);
400 if (self->map_handle != NULL) {
401 self->data = (char *) MapViewOfFile (self->map_handle,
402 FILE_MAP_WRITE,
406 if (self->data != NULL) {
407 self->size = new_size;
408 Py_INCREF (Py_None);
409 return Py_None;
410 } else {
411 dwErrCode = GetLastError();
413 } else {
414 dwErrCode = GetLastError();
416 PyErr_SetFromWindowsErr(dwErrCode);
417 return (NULL);
418 #endif /* MS_WINDOWS */
420 #ifdef UNIX
421 #ifndef HAVE_MREMAP
422 } else {
423 PyErr_SetString(PyExc_SystemError,
424 "mmap: resizing not available--no mremap()");
425 return NULL;
426 #else
427 } else {
428 void *newmap;
430 if (ftruncate(self->fd, new_size) == -1) {
431 PyErr_SetFromErrno(mmap_module_error);
432 return NULL;
435 #ifdef MREMAP_MAYMOVE
436 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
437 #else
438 newmap = mremap(self->data, self->size, new_size, 0);
439 #endif
440 if (newmap == (void *)-1)
442 PyErr_SetFromErrno(mmap_module_error);
443 return NULL;
445 self->data = newmap;
446 self->size = new_size;
447 Py_INCREF(Py_None);
448 return Py_None;
449 #endif /* HAVE_MREMAP */
450 #endif /* UNIX */
454 static PyObject *
455 mmap_tell_method(mmap_object *self, PyObject *args)
457 CHECK_VALID(NULL);
458 if (!PyArg_ParseTuple(args, ":tell"))
459 return NULL;
460 return (Py_BuildValue ("l", (long) self->pos) );
463 static PyObject *
464 mmap_flush_method(mmap_object *self, PyObject *args)
466 unsigned long offset = 0;
467 unsigned long size = self->size;
468 CHECK_VALID(NULL);
469 if (!PyArg_ParseTuple (args, "|kk:flush", &offset, &size)) {
470 return NULL;
471 } else if ((offset + size) > self->size) {
472 PyErr_SetString (PyExc_ValueError,
473 "flush values out of range");
474 return NULL;
475 } else {
476 #ifdef MS_WINDOWS
477 return (Py_BuildValue("l", (long)
478 FlushViewOfFile(self->data+offset, size)));
479 #endif /* MS_WINDOWS */
480 #ifdef UNIX
481 /* XXX semantics of return value? */
482 /* XXX flags for msync? */
483 if (-1 == msync(self->data + offset, size,
484 MS_SYNC))
486 PyErr_SetFromErrno(mmap_module_error);
487 return NULL;
489 return Py_BuildValue ("l", (long) 0);
490 #endif /* UNIX */
494 static PyObject *
495 mmap_seek_method(mmap_object *self, PyObject *args)
497 int dist;
498 int how=0;
499 CHECK_VALID(NULL);
500 if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) {
501 return(NULL);
502 } else {
503 size_t where;
504 switch (how) {
505 case 0: /* relative to start */
506 if (dist < 0)
507 goto onoutofrange;
508 where = dist;
509 break;
510 case 1: /* relative to current position */
511 if ((int)self->pos + dist < 0)
512 goto onoutofrange;
513 where = self->pos + dist;
514 break;
515 case 2: /* relative to end */
516 if ((int)self->size + dist < 0)
517 goto onoutofrange;
518 where = self->size + dist;
519 break;
520 default:
521 PyErr_SetString (PyExc_ValueError,
522 "unknown seek type");
523 return NULL;
525 if (where > self->size)
526 goto onoutofrange;
527 self->pos = where;
528 Py_INCREF (Py_None);
529 return (Py_None);
532 onoutofrange:
533 PyErr_SetString (PyExc_ValueError, "seek out of range");
534 return NULL;
537 static PyObject *
538 mmap_move_method(mmap_object *self, PyObject *args)
540 unsigned long dest, src, count;
541 CHECK_VALID(NULL);
542 if (!PyArg_ParseTuple (args, "kkk:move", &dest, &src, &count) ||
543 !is_writeable(self)) {
544 return NULL;
545 } else {
546 /* bounds check the values */
547 if (/* end of source after end of data?? */
548 ((src+count) > self->size)
549 /* dest will fit? */
550 || (dest+count > self->size)) {
551 PyErr_SetString (PyExc_ValueError,
552 "source or destination out of range");
553 return NULL;
554 } else {
555 memmove (self->data+dest, self->data+src, count);
556 Py_INCREF (Py_None);
557 return Py_None;
562 static struct PyMethodDef mmap_object_methods[] = {
563 {"close", (PyCFunction) mmap_close_method, METH_VARARGS},
564 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
565 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
566 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
567 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
568 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_VARARGS},
569 {"readline", (PyCFunction) mmap_read_line_method, METH_VARARGS},
570 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
571 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
572 {"size", (PyCFunction) mmap_size_method, METH_VARARGS},
573 {"tell", (PyCFunction) mmap_tell_method, METH_VARARGS},
574 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
575 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
576 {NULL, NULL} /* sentinel */
579 /* Functions for treating an mmap'ed file as a buffer */
581 static int
582 mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr)
584 CHECK_VALID(-1);
585 if ( index != 0 ) {
586 PyErr_SetString(PyExc_SystemError,
587 "Accessing non-existent mmap segment");
588 return -1;
590 *ptr = self->data;
591 return self->size;
594 static int
595 mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
597 CHECK_VALID(-1);
598 if ( index != 0 ) {
599 PyErr_SetString(PyExc_SystemError,
600 "Accessing non-existent mmap segment");
601 return -1;
603 if (!is_writeable(self))
604 return -1;
605 *ptr = self->data;
606 return self->size;
609 static int
610 mmap_buffer_getsegcount(mmap_object *self, int *lenp)
612 CHECK_VALID(-1);
613 if (lenp)
614 *lenp = self->size;
615 return 1;
618 static int
619 mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr)
621 if ( index != 0 ) {
622 PyErr_SetString(PyExc_SystemError,
623 "accessing non-existent buffer segment");
624 return -1;
626 *ptr = (const char *)self->data;
627 return self->size;
630 static PyObject *
631 mmap_object_getattr(mmap_object *self, char *name)
633 return Py_FindMethod (mmap_object_methods, (PyObject *)self, name);
636 static int
637 mmap_length(mmap_object *self)
639 CHECK_VALID(-1);
640 return self->size;
643 static PyObject *
644 mmap_item(mmap_object *self, int i)
646 CHECK_VALID(NULL);
647 if (i < 0 || (size_t)i >= self->size) {
648 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
649 return NULL;
651 return PyString_FromStringAndSize(self->data + i, 1);
654 static PyObject *
655 mmap_slice(mmap_object *self, int ilow, int ihigh)
657 CHECK_VALID(NULL);
658 if (ilow < 0)
659 ilow = 0;
660 else if ((size_t)ilow > self->size)
661 ilow = self->size;
662 if (ihigh < 0)
663 ihigh = 0;
664 if (ihigh < ilow)
665 ihigh = ilow;
666 else if ((size_t)ihigh > self->size)
667 ihigh = self->size;
669 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
672 static PyObject *
673 mmap_concat(mmap_object *self, PyObject *bb)
675 CHECK_VALID(NULL);
676 PyErr_SetString(PyExc_SystemError,
677 "mmaps don't support concatenation");
678 return NULL;
681 static PyObject *
682 mmap_repeat(mmap_object *self, int n)
684 CHECK_VALID(NULL);
685 PyErr_SetString(PyExc_SystemError,
686 "mmaps don't support repeat operation");
687 return NULL;
690 static int
691 mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
693 const char *buf;
695 CHECK_VALID(-1);
696 if (ilow < 0)
697 ilow = 0;
698 else if ((size_t)ilow > self->size)
699 ilow = self->size;
700 if (ihigh < 0)
701 ihigh = 0;
702 if (ihigh < ilow)
703 ihigh = ilow;
704 else if ((size_t)ihigh > self->size)
705 ihigh = self->size;
707 if (v == NULL) {
708 PyErr_SetString(PyExc_TypeError,
709 "mmap object doesn't support slice deletion");
710 return -1;
712 if (! (PyString_Check(v)) ) {
713 PyErr_SetString(PyExc_IndexError,
714 "mmap slice assignment must be a string");
715 return -1;
717 if ( PyString_Size(v) != (ihigh - ilow) ) {
718 PyErr_SetString(PyExc_IndexError,
719 "mmap slice assignment is wrong size");
720 return -1;
722 if (!is_writeable(self))
723 return -1;
724 buf = PyString_AsString(v);
725 memcpy(self->data + ilow, buf, ihigh-ilow);
726 return 0;
729 static int
730 mmap_ass_item(mmap_object *self, int i, PyObject *v)
732 const char *buf;
734 CHECK_VALID(-1);
735 if (i < 0 || (size_t)i >= self->size) {
736 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
737 return -1;
739 if (v == NULL) {
740 PyErr_SetString(PyExc_TypeError,
741 "mmap object doesn't support item deletion");
742 return -1;
744 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
745 PyErr_SetString(PyExc_IndexError,
746 "mmap assignment must be single-character string");
747 return -1;
749 if (!is_writeable(self))
750 return -1;
751 buf = PyString_AsString(v);
752 self->data[i] = buf[0];
753 return 0;
756 static PySequenceMethods mmap_as_sequence = {
757 (inquiry)mmap_length, /*sq_length*/
758 (binaryfunc)mmap_concat, /*sq_concat*/
759 (intargfunc)mmap_repeat, /*sq_repeat*/
760 (intargfunc)mmap_item, /*sq_item*/
761 (intintargfunc)mmap_slice, /*sq_slice*/
762 (intobjargproc)mmap_ass_item, /*sq_ass_item*/
763 (intintobjargproc)mmap_ass_slice, /*sq_ass_slice*/
766 static PyBufferProcs mmap_as_buffer = {
767 (getreadbufferproc)mmap_buffer_getreadbuf,
768 (getwritebufferproc)mmap_buffer_getwritebuf,
769 (getsegcountproc)mmap_buffer_getsegcount,
770 (getcharbufferproc)mmap_buffer_getcharbuffer,
773 static PyTypeObject mmap_object_type = {
774 PyObject_HEAD_INIT(0) /* patched in module init */
775 0, /* ob_size */
776 "mmap.mmap", /* tp_name */
777 sizeof(mmap_object), /* tp_size */
778 0, /* tp_itemsize */
779 /* methods */
780 (destructor) mmap_object_dealloc, /* tp_dealloc */
781 0, /* tp_print */
782 (getattrfunc) mmap_object_getattr, /* tp_getattr */
783 0, /* tp_setattr */
784 0, /* tp_compare */
785 0, /* tp_repr */
786 0, /* tp_as_number */
787 &mmap_as_sequence, /*tp_as_sequence*/
788 0, /*tp_as_mapping*/
789 0, /*tp_hash*/
790 0, /*tp_call*/
791 0, /*tp_str*/
792 0, /*tp_getattro*/
793 0, /*tp_setattro*/
794 &mmap_as_buffer, /*tp_as_buffer*/
795 Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
796 0, /*tp_doc*/
800 /* extract the map size from the given PyObject
802 The map size is restricted to [0, INT_MAX] because this is the current
803 Python limitation on object sizes. Although the mmap object *could* handle
804 a larger map size, there is no point because all the useful operations
805 (len(), slicing(), sequence indexing) are limited by a C int.
807 Returns -1 on error, with an appropriate Python exception raised. On
808 success, the map size is returned. */
809 static int
810 _GetMapSize(PyObject *o)
812 if (PyInt_Check(o)) {
813 long i = PyInt_AsLong(o);
814 if (PyErr_Occurred())
815 return -1;
816 if (i < 0)
817 goto onnegoverflow;
818 if (i > INT_MAX)
819 goto onposoverflow;
820 return (int)i;
822 else if (PyLong_Check(o)) {
823 long i = PyLong_AsLong(o);
824 if (PyErr_Occurred()) {
825 /* yes negative overflow is mistaken for positive overflow
826 but not worth the trouble to check sign of 'i' */
827 if (PyErr_ExceptionMatches(PyExc_OverflowError))
828 goto onposoverflow;
829 else
830 return -1;
832 if (i < 0)
833 goto onnegoverflow;
834 if (i > INT_MAX)
835 goto onposoverflow;
836 return (int)i;
838 else {
839 PyErr_SetString(PyExc_TypeError,
840 "map size must be an integral value");
841 return -1;
844 onnegoverflow:
845 PyErr_SetString(PyExc_OverflowError,
846 "memory mapped size must be positive");
847 return -1;
849 onposoverflow:
850 PyErr_SetString(PyExc_OverflowError,
851 "memory mapped size is too large (limited by C int)");
852 return -1;
855 #ifdef UNIX
856 static PyObject *
857 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
859 #ifdef HAVE_FSTAT
860 struct stat st;
861 #endif
862 mmap_object *m_obj;
863 PyObject *map_size_obj = NULL;
864 int map_size;
865 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
866 int access = (int)ACCESS_DEFAULT;
867 static const char *keywords[] = {"fileno", "length",
868 "flags", "prot",
869 "access", NULL};
871 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords,
872 &fd, &map_size_obj, &flags, &prot,
873 &access))
874 return NULL;
875 map_size = _GetMapSize(map_size_obj);
876 if (map_size < 0)
877 return NULL;
879 if ((access != (int)ACCESS_DEFAULT) &&
880 ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ))))
881 return PyErr_Format(PyExc_ValueError,
882 "mmap can't specify both access and flags, prot.");
883 switch((access_mode)access) {
884 case ACCESS_READ:
885 flags = MAP_SHARED;
886 prot = PROT_READ;
887 break;
888 case ACCESS_WRITE:
889 flags = MAP_SHARED;
890 prot = PROT_READ | PROT_WRITE;
891 break;
892 case ACCESS_COPY:
893 flags = MAP_PRIVATE;
894 prot = PROT_READ | PROT_WRITE;
895 break;
896 case ACCESS_DEFAULT:
897 /* use the specified or default values of flags and prot */
898 break;
899 default:
900 return PyErr_Format(PyExc_ValueError,
901 "mmap invalid access parameter.");
904 #ifdef HAVE_FSTAT
905 # ifdef __VMS
906 /* on OpenVMS we must ensure that all bytes are written to the file */
907 fsync(fd);
908 # endif
909 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
910 if (map_size == 0) {
911 map_size = (int)st.st_size;
912 } else if ((size_t)map_size > st.st_size) {
913 PyErr_SetString(PyExc_ValueError,
914 "mmap length is greater than file size");
915 return NULL;
918 #endif
919 m_obj = PyObject_New (mmap_object, &mmap_object_type);
920 if (m_obj == NULL) {return NULL;}
921 m_obj->data = NULL;
922 m_obj->size = (size_t) map_size;
923 m_obj->pos = (size_t) 0;
924 m_obj->fd = dup(fd);
925 if (m_obj->fd == -1) {
926 Py_DECREF(m_obj);
927 PyErr_SetFromErrno(mmap_module_error);
928 return NULL;
930 m_obj->data = mmap(NULL, map_size,
931 prot, flags,
932 fd, 0);
933 if (m_obj->data == (char *)-1) {
934 m_obj->data = NULL;
935 Py_DECREF(m_obj);
936 PyErr_SetFromErrno(mmap_module_error);
937 return NULL;
939 m_obj->access = (access_mode)access;
940 return (PyObject *)m_obj;
942 #endif /* UNIX */
944 #ifdef MS_WINDOWS
945 static PyObject *
946 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
948 mmap_object *m_obj;
949 PyObject *map_size_obj = NULL;
950 int map_size;
951 char *tagname = "";
952 DWORD dwErr = 0;
953 int fileno;
954 HANDLE fh = 0;
955 int access = (access_mode)ACCESS_DEFAULT;
956 DWORD flProtect, dwDesiredAccess;
957 static const char *keywords[] = { "fileno", "length",
958 "tagname",
959 "access", NULL };
961 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
962 &fileno, &map_size_obj,
963 &tagname, &access)) {
964 return NULL;
967 switch((access_mode)access) {
968 case ACCESS_READ:
969 flProtect = PAGE_READONLY;
970 dwDesiredAccess = FILE_MAP_READ;
971 break;
972 case ACCESS_DEFAULT: case ACCESS_WRITE:
973 flProtect = PAGE_READWRITE;
974 dwDesiredAccess = FILE_MAP_WRITE;
975 break;
976 case ACCESS_COPY:
977 flProtect = PAGE_WRITECOPY;
978 dwDesiredAccess = FILE_MAP_COPY;
979 break;
980 default:
981 return PyErr_Format(PyExc_ValueError,
982 "mmap invalid access parameter.");
985 map_size = _GetMapSize(map_size_obj);
986 if (map_size < 0)
987 return NULL;
989 /* if an actual filename has been specified */
990 if (fileno != 0) {
991 fh = (HANDLE)_get_osfhandle(fileno);
992 if (fh==(HANDLE)-1) {
993 PyErr_SetFromErrno(mmap_module_error);
994 return NULL;
996 /* Win9x appears to need us seeked to zero */
997 lseek(fileno, 0, SEEK_SET);
1000 m_obj = PyObject_New (mmap_object, &mmap_object_type);
1001 if (m_obj==NULL)
1002 return NULL;
1003 /* Set every field to an invalid marker, so we can safely
1004 destruct the object in the face of failure */
1005 m_obj->data = NULL;
1006 m_obj->file_handle = INVALID_HANDLE_VALUE;
1007 m_obj->map_handle = INVALID_HANDLE_VALUE;
1008 m_obj->tagname = NULL;
1010 if (fh) {
1011 /* It is necessary to duplicate the handle, so the
1012 Python code can close it on us */
1013 if (!DuplicateHandle(
1014 GetCurrentProcess(), /* source process handle */
1015 fh, /* handle to be duplicated */
1016 GetCurrentProcess(), /* target proc handle */
1017 (LPHANDLE)&m_obj->file_handle, /* result */
1018 0, /* access - ignored due to options value */
1019 FALSE, /* inherited by child processes? */
1020 DUPLICATE_SAME_ACCESS)) { /* options */
1021 dwErr = GetLastError();
1022 Py_DECREF(m_obj);
1023 PyErr_SetFromWindowsErr(dwErr);
1024 return NULL;
1026 if (!map_size) {
1027 m_obj->size = GetFileSize (fh, NULL);
1028 } else {
1029 m_obj->size = map_size;
1032 else {
1033 m_obj->size = map_size;
1036 /* set the initial position */
1037 m_obj->pos = (size_t) 0;
1039 /* set the tag name */
1040 if (tagname != NULL && *tagname != '\0') {
1041 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1042 if (m_obj->tagname == NULL) {
1043 PyErr_NoMemory();
1044 Py_DECREF(m_obj);
1045 return NULL;
1047 strcpy(m_obj->tagname, tagname);
1049 else
1050 m_obj->tagname = NULL;
1052 m_obj->access = (access_mode)access;
1053 m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
1054 NULL,
1055 flProtect,
1057 m_obj->size,
1058 m_obj->tagname);
1059 if (m_obj->map_handle != NULL) {
1060 m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
1061 dwDesiredAccess,
1065 if (m_obj->data != NULL) {
1066 return ((PyObject *) m_obj);
1067 } else {
1068 dwErr = GetLastError();
1070 } else {
1071 dwErr = GetLastError();
1073 Py_DECREF(m_obj);
1074 PyErr_SetFromWindowsErr(dwErr);
1075 return (NULL);
1077 #endif /* MS_WINDOWS */
1079 /* List of functions exported by this module */
1080 static struct PyMethodDef mmap_functions[] = {
1081 {"mmap", (PyCFunction) new_mmap_object,
1082 METH_VARARGS|METH_KEYWORDS},
1083 {NULL, NULL} /* Sentinel */
1086 PyMODINIT_FUNC
1087 initmmap(void)
1089 PyObject *dict, *module;
1091 /* Patch the object type */
1092 mmap_object_type.ob_type = &PyType_Type;
1094 module = Py_InitModule ("mmap", mmap_functions);
1095 dict = PyModule_GetDict (module);
1096 mmap_module_error = PyExc_EnvironmentError;
1097 Py_INCREF(mmap_module_error);
1098 PyDict_SetItemString (dict, "error", mmap_module_error);
1099 #ifdef PROT_EXEC
1100 PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) );
1101 #endif
1102 #ifdef PROT_READ
1103 PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) );
1104 #endif
1105 #ifdef PROT_WRITE
1106 PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) );
1107 #endif
1109 #ifdef MAP_SHARED
1110 PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) );
1111 #endif
1112 #ifdef MAP_PRIVATE
1113 PyDict_SetItemString (dict, "MAP_PRIVATE",
1114 PyInt_FromLong(MAP_PRIVATE) );
1115 #endif
1116 #ifdef MAP_DENYWRITE
1117 PyDict_SetItemString (dict, "MAP_DENYWRITE",
1118 PyInt_FromLong(MAP_DENYWRITE) );
1119 #endif
1120 #ifdef MAP_EXECUTABLE
1121 PyDict_SetItemString (dict, "MAP_EXECUTABLE",
1122 PyInt_FromLong(MAP_EXECUTABLE) );
1123 #endif
1124 #ifdef MAP_ANON
1125 PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANON) );
1126 PyDict_SetItemString (dict, "MAP_ANONYMOUS",
1127 PyInt_FromLong(MAP_ANON) );
1128 #endif
1130 PyDict_SetItemString (dict, "PAGESIZE",
1131 PyInt_FromLong( (long)my_getpagesize() ) );
1133 PyDict_SetItemString (dict, "ACCESS_READ",
1134 PyInt_FromLong(ACCESS_READ));
1135 PyDict_SetItemString (dict, "ACCESS_WRITE",
1136 PyInt_FromLong(ACCESS_WRITE));
1137 PyDict_SetItemString (dict, "ACCESS_COPY",
1138 PyInt_FromLong(ACCESS_COPY));