Maintain backwards compatibility with python < 2.3 by dynamically
[python/dscho.git] / Modules / mmapmodule.c
bloba61a37a10c664db3e9bbeed3dde5abcb8a20c4c2
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 #ifndef MS_SYNC
43 /* This is missing e.g. on SunOS 4.1.4 */
44 #define MS_SYNC 0
45 #endif
47 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
48 static int
49 my_getpagesize(void)
51 return sysconf(_SC_PAGESIZE);
53 #else
54 #define my_getpagesize getpagesize
55 #endif
57 #endif /* UNIX */
59 #include <string.h>
60 #include <sys/types.h>
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->data!=NULL) {
108 msync(m_obj->data, m_obj->size, MS_SYNC);
109 munmap(m_obj->data, m_obj->size);
111 #endif /* UNIX */
113 PyObject_Del(m_obj);
116 static PyObject *
117 mmap_close_method(mmap_object *self, PyObject *args)
119 if (!PyArg_ParseTuple(args, ":close"))
120 return NULL;
121 #ifdef MS_WINDOWS
122 /* For each resource we maintain, we need to check
123 the value is valid, and if so, free the resource
124 and set the member value to an invalid value so
125 the dealloc does not attempt to resource clearing
126 again.
127 TODO - should we check for errors in the close operations???
129 if (self->data != NULL) {
130 UnmapViewOfFile (self->data);
131 self->data = NULL;
133 if (self->map_handle != INVALID_HANDLE_VALUE) {
134 CloseHandle (self->map_handle);
135 self->map_handle = INVALID_HANDLE_VALUE;
137 if (self->file_handle != INVALID_HANDLE_VALUE) {
138 CloseHandle (self->file_handle);
139 self->file_handle = INVALID_HANDLE_VALUE;
141 #endif /* MS_WINDOWS */
143 #ifdef UNIX
144 if (self->data != NULL) {
145 munmap(self->data, self->size);
146 self->data = NULL;
148 #endif
150 Py_INCREF (Py_None);
151 return (Py_None);
154 #ifdef MS_WINDOWS
155 #define CHECK_VALID(err) \
156 do { \
157 if (self->map_handle == INVALID_HANDLE_VALUE) { \
158 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
159 return err; \
161 } while (0)
162 #endif /* MS_WINDOWS */
164 #ifdef UNIX
165 #define CHECK_VALID(err) \
166 do { \
167 if (self->data == NULL) { \
168 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
169 return err; \
171 } while (0)
172 #endif /* UNIX */
174 static PyObject *
175 mmap_read_byte_method(mmap_object *self,
176 PyObject *args)
178 CHECK_VALID(NULL);
179 if (!PyArg_ParseTuple(args, ":read_byte"))
180 return NULL;
181 if (self->pos < self->size) {
182 char value = self->data[self->pos];
183 self->pos += 1;
184 return Py_BuildValue("c", value);
185 } else {
186 PyErr_SetString (PyExc_ValueError, "read byte out of range");
187 return NULL;
191 static PyObject *
192 mmap_read_line_method(mmap_object *self,
193 PyObject *args)
195 char *start = self->data+self->pos;
196 char *eof = self->data+self->size;
197 char *eol;
198 PyObject *result;
200 CHECK_VALID(NULL);
201 if (!PyArg_ParseTuple(args, ":readline"))
202 return NULL;
204 eol = memchr(start, '\n', self->size - self->pos);
205 if (!eol)
206 eol = eof;
207 else
208 ++eol; /* we're interested in the position after the
209 newline. */
210 result = PyString_FromStringAndSize(start, (eol - start));
211 self->pos += (eol - start);
212 return (result);
215 static PyObject *
216 mmap_read_method(mmap_object *self,
217 PyObject *args)
219 long num_bytes;
220 PyObject *result;
222 CHECK_VALID(NULL);
223 if (!PyArg_ParseTuple(args, "l:read", &num_bytes))
224 return(NULL);
226 /* silently 'adjust' out-of-range requests */
227 if ((self->pos + num_bytes) > self->size) {
228 num_bytes -= (self->pos+num_bytes) - self->size;
230 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
231 self->pos += num_bytes;
232 return (result);
235 static PyObject *
236 mmap_find_method(mmap_object *self,
237 PyObject *args)
239 long start = self->pos;
240 char *needle;
241 int len;
243 CHECK_VALID(NULL);
244 if (!PyArg_ParseTuple (args, "s#|l:find", &needle, &len, &start)) {
245 return NULL;
246 } else {
247 char *p;
248 char *e = self->data + self->size;
250 if (start < 0)
251 start += self->size;
252 if (start < 0)
253 start = 0;
254 else if ((size_t)start > self->size)
255 start = self->size;
257 for (p = self->data + start; p + len <= e; ++p) {
258 int i;
259 for (i = 0; i < len && needle[i] == p[i]; ++i)
260 /* nothing */;
261 if (i == len) {
262 return Py_BuildValue (
263 "l",
264 (long) (p - self->data));
267 return Py_BuildValue ("l", (long) -1);
271 static int
272 is_writeable(mmap_object *self)
274 if (self->access != ACCESS_READ)
275 return 1;
276 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
277 return 0;
280 static int
281 is_resizeable(mmap_object *self)
283 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
284 return 1;
285 PyErr_Format(PyExc_TypeError,
286 "mmap can't resize a readonly or copy-on-write memory map.");
287 return 0;
291 static PyObject *
292 mmap_write_method(mmap_object *self,
293 PyObject *args)
295 int length;
296 char *data;
298 CHECK_VALID(NULL);
299 if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
300 return(NULL);
302 if (!is_writeable(self))
303 return NULL;
305 if ((self->pos + length) > self->size) {
306 PyErr_SetString (PyExc_ValueError, "data out of range");
307 return NULL;
309 memcpy (self->data+self->pos, data, length);
310 self->pos = self->pos+length;
311 Py_INCREF (Py_None);
312 return (Py_None);
315 static PyObject *
316 mmap_write_byte_method(mmap_object *self,
317 PyObject *args)
319 char value;
321 CHECK_VALID(NULL);
322 if (!PyArg_ParseTuple (args, "c:write_byte", &value))
323 return(NULL);
325 if (!is_writeable(self))
326 return NULL;
327 *(self->data+self->pos) = value;
328 self->pos += 1;
329 Py_INCREF (Py_None);
330 return (Py_None);
333 static PyObject *
334 mmap_size_method(mmap_object *self,
335 PyObject *args)
337 CHECK_VALID(NULL);
338 if (!PyArg_ParseTuple(args, ":size"))
339 return NULL;
341 #ifdef MS_WINDOWS
342 if (self->file_handle != INVALID_HANDLE_VALUE) {
343 return (Py_BuildValue (
344 "l", (long)
345 GetFileSize (self->file_handle, NULL)));
346 } else {
347 return (Py_BuildValue ("l", (long) self->size) );
349 #endif /* MS_WINDOWS */
351 #ifdef UNIX
353 struct stat buf;
354 if (-1 == fstat(self->fd, &buf)) {
355 PyErr_SetFromErrno(mmap_module_error);
356 return NULL;
358 return (Py_BuildValue ("l", (long) buf.st_size) );
360 #endif /* UNIX */
363 /* This assumes that you want the entire file mapped,
364 / and when recreating the map will make the new file
365 / have the new size
367 / Is this really necessary? This could easily be done
368 / from python by just closing and re-opening with the
369 / new size?
372 static PyObject *
373 mmap_resize_method(mmap_object *self,
374 PyObject *args)
376 unsigned long new_size;
377 CHECK_VALID(NULL);
378 if (!PyArg_ParseTuple (args, "l:resize", &new_size) ||
379 !is_resizeable(self)) {
380 return NULL;
381 #ifdef MS_WINDOWS
382 } else {
383 DWORD dwErrCode = 0;
384 /* First, unmap the file view */
385 UnmapViewOfFile (self->data);
386 /* Close the mapping object */
387 CloseHandle (self->map_handle);
388 /* Move to the desired EOF position */
389 SetFilePointer (self->file_handle,
390 new_size, NULL, FILE_BEGIN);
391 /* Change the size of the file */
392 SetEndOfFile (self->file_handle);
393 /* Create another mapping object and remap the file view */
394 self->map_handle = CreateFileMapping (
395 self->file_handle,
396 NULL,
397 PAGE_READWRITE,
399 new_size,
400 self->tagname);
401 if (self->map_handle != NULL) {
402 self->data = (char *) MapViewOfFile (self->map_handle,
403 FILE_MAP_WRITE,
407 if (self->data != NULL) {
408 self->size = new_size;
409 Py_INCREF (Py_None);
410 return Py_None;
411 } else {
412 dwErrCode = GetLastError();
414 } else {
415 dwErrCode = GetLastError();
417 PyErr_SetFromWindowsErr(dwErrCode);
418 return (NULL);
419 #endif /* MS_WINDOWS */
421 #ifdef UNIX
422 #ifndef HAVE_MREMAP
423 } else {
424 PyErr_SetString(PyExc_SystemError,
425 "mmap: resizing not available--no mremap()");
426 return NULL;
427 #else
428 } else {
429 void *newmap;
431 #ifdef MREMAP_MAYMOVE
432 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
433 #else
434 newmap = mremap(self->data, self->size, new_size, 0);
435 #endif
436 if (newmap == (void *)-1)
438 PyErr_SetFromErrno(mmap_module_error);
439 return NULL;
441 self->data = newmap;
442 self->size = new_size;
443 Py_INCREF(Py_None);
444 return Py_None;
445 #endif /* HAVE_MREMAP */
446 #endif /* UNIX */
450 static PyObject *
451 mmap_tell_method(mmap_object *self, PyObject *args)
453 CHECK_VALID(NULL);
454 if (!PyArg_ParseTuple(args, ":tell"))
455 return NULL;
456 return (Py_BuildValue ("l", (long) self->pos) );
459 static PyObject *
460 mmap_flush_method(mmap_object *self, PyObject *args)
462 size_t offset = 0;
463 size_t size = self->size;
464 CHECK_VALID(NULL);
465 if (!PyArg_ParseTuple (args, "|ll:flush", &offset, &size)) {
466 return NULL;
467 } else if ((offset + size) > self->size) {
468 PyErr_SetString (PyExc_ValueError,
469 "flush values out of range");
470 return NULL;
471 } else {
472 #ifdef MS_WINDOWS
473 return (Py_BuildValue("l", (long)
474 FlushViewOfFile(self->data+offset, size)));
475 #endif /* MS_WINDOWS */
476 #ifdef UNIX
477 /* XXX semantics of return value? */
478 /* XXX flags for msync? */
479 if (-1 == msync(self->data + offset, size,
480 MS_SYNC))
482 PyErr_SetFromErrno(mmap_module_error);
483 return NULL;
485 return Py_BuildValue ("l", (long) 0);
486 #endif /* UNIX */
490 static PyObject *
491 mmap_seek_method(mmap_object *self, PyObject *args)
493 int dist;
494 int how=0;
495 CHECK_VALID(NULL);
496 if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) {
497 return(NULL);
498 } else {
499 size_t where;
500 switch (how) {
501 case 0: /* relative to start */
502 if (dist < 0)
503 goto onoutofrange;
504 where = dist;
505 break;
506 case 1: /* relative to current position */
507 if ((int)self->pos + dist < 0)
508 goto onoutofrange;
509 where = self->pos + dist;
510 break;
511 case 2: /* relative to end */
512 if ((int)self->size + dist < 0)
513 goto onoutofrange;
514 where = self->size + dist;
515 break;
516 default:
517 PyErr_SetString (PyExc_ValueError,
518 "unknown seek type");
519 return NULL;
521 if (where > self->size)
522 goto onoutofrange;
523 self->pos = where;
524 Py_INCREF (Py_None);
525 return (Py_None);
528 onoutofrange:
529 PyErr_SetString (PyExc_ValueError, "seek out of range");
530 return NULL;
533 static PyObject *
534 mmap_move_method(mmap_object *self, PyObject *args)
536 unsigned long dest, src, count;
537 CHECK_VALID(NULL);
538 if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count) ||
539 !is_writeable(self)) {
540 return NULL;
541 } else {
542 /* bounds check the values */
543 if (/* end of source after end of data?? */
544 ((src+count) > self->size)
545 /* dest will fit? */
546 || (dest+count > self->size)) {
547 PyErr_SetString (PyExc_ValueError,
548 "source or destination out of range");
549 return NULL;
550 } else {
551 memmove (self->data+dest, self->data+src, count);
552 Py_INCREF (Py_None);
553 return Py_None;
558 static struct PyMethodDef mmap_object_methods[] = {
559 {"close", (PyCFunction) mmap_close_method, METH_VARARGS},
560 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
561 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
562 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
563 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
564 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_VARARGS},
565 {"readline", (PyCFunction) mmap_read_line_method, METH_VARARGS},
566 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
567 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
568 {"size", (PyCFunction) mmap_size_method, METH_VARARGS},
569 {"tell", (PyCFunction) mmap_tell_method, METH_VARARGS},
570 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
571 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
572 {NULL, NULL} /* sentinel */
575 /* Functions for treating an mmap'ed file as a buffer */
577 static int
578 mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr)
580 CHECK_VALID(-1);
581 if ( index != 0 ) {
582 PyErr_SetString(PyExc_SystemError,
583 "Accessing non-existent mmap segment");
584 return -1;
586 *ptr = self->data;
587 return self->size;
590 static int
591 mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
593 CHECK_VALID(-1);
594 if ( index != 0 ) {
595 PyErr_SetString(PyExc_SystemError,
596 "Accessing non-existent mmap segment");
597 return -1;
599 if (!is_writeable(self))
600 return -1;
601 *ptr = self->data;
602 return self->size;
605 static int
606 mmap_buffer_getsegcount(mmap_object *self, int *lenp)
608 CHECK_VALID(-1);
609 if (lenp)
610 *lenp = self->size;
611 return 1;
614 static int
615 mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr)
617 if ( index != 0 ) {
618 PyErr_SetString(PyExc_SystemError,
619 "accessing non-existent buffer segment");
620 return -1;
622 *ptr = (const char *)self->data;
623 return self->size;
626 static PyObject *
627 mmap_object_getattr(mmap_object *self, char *name)
629 return Py_FindMethod (mmap_object_methods, (PyObject *)self, name);
632 static int
633 mmap_length(mmap_object *self)
635 CHECK_VALID(-1);
636 return self->size;
639 static PyObject *
640 mmap_item(mmap_object *self, int i)
642 CHECK_VALID(NULL);
643 if (i < 0 || (size_t)i >= self->size) {
644 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
645 return NULL;
647 return PyString_FromStringAndSize(self->data + i, 1);
650 static PyObject *
651 mmap_slice(mmap_object *self, int ilow, int ihigh)
653 CHECK_VALID(NULL);
654 if (ilow < 0)
655 ilow = 0;
656 else if ((size_t)ilow > self->size)
657 ilow = self->size;
658 if (ihigh < 0)
659 ihigh = 0;
660 if (ihigh < ilow)
661 ihigh = ilow;
662 else if ((size_t)ihigh > self->size)
663 ihigh = self->size;
665 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
668 static PyObject *
669 mmap_concat(mmap_object *self, PyObject *bb)
671 CHECK_VALID(NULL);
672 PyErr_SetString(PyExc_SystemError,
673 "mmaps don't support concatenation");
674 return NULL;
677 static PyObject *
678 mmap_repeat(mmap_object *self, int n)
680 CHECK_VALID(NULL);
681 PyErr_SetString(PyExc_SystemError,
682 "mmaps don't support repeat operation");
683 return NULL;
686 static int
687 mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
689 const char *buf;
691 CHECK_VALID(-1);
692 if (ilow < 0)
693 ilow = 0;
694 else if ((size_t)ilow > self->size)
695 ilow = self->size;
696 if (ihigh < 0)
697 ihigh = 0;
698 if (ihigh < ilow)
699 ihigh = ilow;
700 else if ((size_t)ihigh > self->size)
701 ihigh = self->size;
703 if (v == NULL) {
704 PyErr_SetString(PyExc_TypeError,
705 "mmap object doesn't support slice deletion");
706 return -1;
708 if (! (PyString_Check(v)) ) {
709 PyErr_SetString(PyExc_IndexError,
710 "mmap slice assignment must be a string");
711 return -1;
713 if ( PyString_Size(v) != (ihigh - ilow) ) {
714 PyErr_SetString(PyExc_IndexError,
715 "mmap slice assignment is wrong size");
716 return -1;
718 if (!is_writeable(self))
719 return -1;
720 buf = PyString_AsString(v);
721 memcpy(self->data + ilow, buf, ihigh-ilow);
722 return 0;
725 static int
726 mmap_ass_item(mmap_object *self, int i, PyObject *v)
728 const char *buf;
730 CHECK_VALID(-1);
731 if (i < 0 || (size_t)i >= self->size) {
732 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
733 return -1;
735 if (v == NULL) {
736 PyErr_SetString(PyExc_TypeError,
737 "mmap object doesn't support item deletion");
738 return -1;
740 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
741 PyErr_SetString(PyExc_IndexError,
742 "mmap assignment must be single-character string");
743 return -1;
745 if (!is_writeable(self))
746 return -1;
747 buf = PyString_AsString(v);
748 self->data[i] = buf[0];
749 return 0;
752 static PySequenceMethods mmap_as_sequence = {
753 (inquiry)mmap_length, /*sq_length*/
754 (binaryfunc)mmap_concat, /*sq_concat*/
755 (intargfunc)mmap_repeat, /*sq_repeat*/
756 (intargfunc)mmap_item, /*sq_item*/
757 (intintargfunc)mmap_slice, /*sq_slice*/
758 (intobjargproc)mmap_ass_item, /*sq_ass_item*/
759 (intintobjargproc)mmap_ass_slice, /*sq_ass_slice*/
762 static PyBufferProcs mmap_as_buffer = {
763 (getreadbufferproc)mmap_buffer_getreadbuf,
764 (getwritebufferproc)mmap_buffer_getwritebuf,
765 (getsegcountproc)mmap_buffer_getsegcount,
766 (getcharbufferproc)mmap_buffer_getcharbuffer,
769 static PyTypeObject mmap_object_type = {
770 PyObject_HEAD_INIT(0) /* patched in module init */
771 0, /* ob_size */
772 "mmap.mmap", /* tp_name */
773 sizeof(mmap_object), /* tp_size */
774 0, /* tp_itemsize */
775 /* methods */
776 (destructor) mmap_object_dealloc, /* tp_dealloc */
777 0, /* tp_print */
778 (getattrfunc) mmap_object_getattr, /* tp_getattr */
779 0, /* tp_setattr */
780 0, /* tp_compare */
781 0, /* tp_repr */
782 0, /* tp_as_number */
783 &mmap_as_sequence, /*tp_as_sequence*/
784 0, /*tp_as_mapping*/
785 0, /*tp_hash*/
786 0, /*tp_call*/
787 0, /*tp_str*/
788 0, /*tp_getattro*/
789 0, /*tp_setattro*/
790 &mmap_as_buffer, /*tp_as_buffer*/
791 Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
792 0, /*tp_doc*/
796 /* extract the map size from the given PyObject
798 The map size is restricted to [0, INT_MAX] because this is the current
799 Python limitation on object sizes. Although the mmap object *could* handle
800 a larger map size, there is no point because all the useful operations
801 (len(), slicing(), sequence indexing) are limited by a C int.
803 Returns -1 on error, with an appropriate Python exception raised. On
804 success, the map size is returned. */
805 static int
806 _GetMapSize(PyObject *o)
808 if (PyInt_Check(o)) {
809 long i = PyInt_AsLong(o);
810 if (PyErr_Occurred())
811 return -1;
812 if (i < 0)
813 goto onnegoverflow;
814 if (i > INT_MAX)
815 goto onposoverflow;
816 return (int)i;
818 else if (PyLong_Check(o)) {
819 long i = PyLong_AsLong(o);
820 if (PyErr_Occurred()) {
821 /* yes negative overflow is mistaken for positive overflow
822 but not worth the trouble to check sign of 'i' */
823 if (PyErr_ExceptionMatches(PyExc_OverflowError))
824 goto onposoverflow;
825 else
826 return -1;
828 if (i < 0)
829 goto onnegoverflow;
830 if (i > INT_MAX)
831 goto onposoverflow;
832 return (int)i;
834 else {
835 PyErr_SetString(PyExc_TypeError,
836 "map size must be an integral value");
837 return -1;
840 onnegoverflow:
841 PyErr_SetString(PyExc_OverflowError,
842 "memory mapped size must be positive");
843 return -1;
845 onposoverflow:
846 PyErr_SetString(PyExc_OverflowError,
847 "memory mapped size is too large (limited by C int)");
848 return -1;
851 #ifdef UNIX
852 static PyObject *
853 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
855 #ifdef HAVE_FSTAT
856 struct stat st;
857 #endif
858 mmap_object *m_obj;
859 PyObject *map_size_obj = NULL;
860 int map_size;
861 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
862 access_mode access = ACCESS_DEFAULT;
863 char *keywords[] = {"fileno", "length",
864 "flags", "prot",
865 "access", NULL};
867 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords,
868 &fd, &map_size_obj, &flags, &prot, &access))
869 return NULL;
870 map_size = _GetMapSize(map_size_obj);
871 if (map_size < 0)
872 return NULL;
874 if ((access != ACCESS_DEFAULT) &&
875 ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ))))
876 return PyErr_Format(PyExc_ValueError,
877 "mmap can't specify both access and flags, prot.");
878 switch(access) {
879 case ACCESS_READ:
880 flags = MAP_SHARED;
881 prot = PROT_READ;
882 break;
883 case ACCESS_WRITE:
884 flags = MAP_SHARED;
885 prot = PROT_READ | PROT_WRITE;
886 break;
887 case ACCESS_COPY:
888 flags = MAP_PRIVATE;
889 prot = PROT_READ | PROT_WRITE;
890 break;
891 case ACCESS_DEFAULT:
892 /* use the specified or default values of flags and prot */
893 break;
894 default:
895 return PyErr_Format(PyExc_ValueError,
896 "mmap invalid access parameter.");
899 #ifdef HAVE_FSTAT
900 # ifdef __VMS
901 /* on OpenVMS we must ensure that all bytes are written to the file */
902 fsync(fd);
903 # endif
904 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
905 (size_t)map_size > st.st_size) {
906 PyErr_SetString(PyExc_ValueError,
907 "mmap length is greater than file size");
908 return NULL;
910 #endif
911 m_obj = PyObject_New (mmap_object, &mmap_object_type);
912 if (m_obj == NULL) {return NULL;}
913 m_obj->size = (size_t) map_size;
914 m_obj->pos = (size_t) 0;
915 m_obj->fd = fd;
916 m_obj->data = mmap(NULL, map_size,
917 prot, flags,
918 fd, 0);
919 if (m_obj->data == (char *)-1) {
920 Py_DECREF(m_obj);
921 PyErr_SetFromErrno(mmap_module_error);
922 return NULL;
924 m_obj->access = access;
925 return (PyObject *)m_obj;
927 #endif /* UNIX */
929 #ifdef MS_WINDOWS
930 static PyObject *
931 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
933 mmap_object *m_obj;
934 PyObject *map_size_obj = NULL;
935 int map_size;
936 char *tagname = "";
937 DWORD dwErr = 0;
938 int fileno;
939 HANDLE fh = 0;
940 access_mode access = ACCESS_DEFAULT;
941 DWORD flProtect, dwDesiredAccess;
942 char *keywords[] = { "fileno", "length",
943 "tagname",
944 "access", NULL };
946 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
947 &fileno, &map_size_obj,
948 &tagname, &access)) {
949 return NULL;
952 switch(access) {
953 case ACCESS_READ:
954 flProtect = PAGE_READONLY;
955 dwDesiredAccess = FILE_MAP_READ;
956 break;
957 case ACCESS_DEFAULT: case ACCESS_WRITE:
958 flProtect = PAGE_READWRITE;
959 dwDesiredAccess = FILE_MAP_WRITE;
960 break;
961 case ACCESS_COPY:
962 flProtect = PAGE_WRITECOPY;
963 dwDesiredAccess = FILE_MAP_COPY;
964 break;
965 default:
966 return PyErr_Format(PyExc_ValueError,
967 "mmap invalid access parameter.");
970 map_size = _GetMapSize(map_size_obj);
971 if (map_size < 0)
972 return NULL;
974 /* if an actual filename has been specified */
975 if (fileno != 0) {
976 fh = (HANDLE)_get_osfhandle(fileno);
977 if (fh==(HANDLE)-1) {
978 PyErr_SetFromErrno(mmap_module_error);
979 return NULL;
981 /* Win9x appears to need us seeked to zero */
982 lseek(fileno, 0, SEEK_SET);
985 m_obj = PyObject_New (mmap_object, &mmap_object_type);
986 if (m_obj==NULL)
987 return NULL;
988 /* Set every field to an invalid marker, so we can safely
989 destruct the object in the face of failure */
990 m_obj->data = NULL;
991 m_obj->file_handle = INVALID_HANDLE_VALUE;
992 m_obj->map_handle = INVALID_HANDLE_VALUE;
993 m_obj->tagname = NULL;
995 if (fh) {
996 /* It is necessary to duplicate the handle, so the
997 Python code can close it on us */
998 if (!DuplicateHandle(
999 GetCurrentProcess(), /* source process handle */
1000 fh, /* handle to be duplicated */
1001 GetCurrentProcess(), /* target proc handle */
1002 (LPHANDLE)&m_obj->file_handle, /* result */
1003 0, /* access - ignored due to options value */
1004 FALSE, /* inherited by child processes? */
1005 DUPLICATE_SAME_ACCESS)) { /* options */
1006 dwErr = GetLastError();
1007 Py_DECREF(m_obj);
1008 PyErr_SetFromWindowsErr(dwErr);
1009 return NULL;
1011 if (!map_size) {
1012 m_obj->size = GetFileSize (fh, NULL);
1013 } else {
1014 m_obj->size = map_size;
1017 else {
1018 m_obj->size = map_size;
1021 /* set the initial position */
1022 m_obj->pos = (size_t) 0;
1024 /* set the tag name */
1025 if (tagname != NULL && *tagname != '\0') {
1026 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1027 if (m_obj->tagname == NULL) {
1028 PyErr_NoMemory();
1029 Py_DECREF(m_obj);
1030 return NULL;
1032 strcpy(m_obj->tagname, tagname);
1034 else
1035 m_obj->tagname = NULL;
1037 m_obj->access = access;
1038 m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
1039 NULL,
1040 flProtect,
1042 m_obj->size,
1043 m_obj->tagname);
1044 if (m_obj->map_handle != NULL) {
1045 m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
1046 dwDesiredAccess,
1050 if (m_obj->data != NULL) {
1051 return ((PyObject *) m_obj);
1052 } else {
1053 dwErr = GetLastError();
1055 } else {
1056 dwErr = GetLastError();
1058 Py_DECREF(m_obj);
1059 PyErr_SetFromWindowsErr(dwErr);
1060 return (NULL);
1062 #endif /* MS_WINDOWS */
1064 /* List of functions exported by this module */
1065 static struct PyMethodDef mmap_functions[] = {
1066 {"mmap", (PyCFunction) new_mmap_object,
1067 METH_VARARGS|METH_KEYWORDS},
1068 {NULL, NULL} /* Sentinel */
1071 PyMODINIT_FUNC
1072 initmmap(void)
1074 PyObject *dict, *module;
1076 /* Patch the object type */
1077 mmap_object_type.ob_type = &PyType_Type;
1079 module = Py_InitModule ("mmap", mmap_functions);
1080 dict = PyModule_GetDict (module);
1081 mmap_module_error = PyExc_EnvironmentError;
1082 Py_INCREF(mmap_module_error);
1083 PyDict_SetItemString (dict, "error", mmap_module_error);
1084 #ifdef PROT_EXEC
1085 PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) );
1086 #endif
1087 #ifdef PROT_READ
1088 PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) );
1089 #endif
1090 #ifdef PROT_WRITE
1091 PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) );
1092 #endif
1094 #ifdef MAP_SHARED
1095 PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) );
1096 #endif
1097 #ifdef MAP_PRIVATE
1098 PyDict_SetItemString (dict, "MAP_PRIVATE",
1099 PyInt_FromLong(MAP_PRIVATE) );
1100 #endif
1101 #ifdef MAP_DENYWRITE
1102 PyDict_SetItemString (dict, "MAP_DENYWRITE",
1103 PyInt_FromLong(MAP_DENYWRITE) );
1104 #endif
1105 #ifdef MAP_EXECUTABLE
1106 PyDict_SetItemString (dict, "MAP_EXECUTABLE",
1107 PyInt_FromLong(MAP_EXECUTABLE) );
1108 #endif
1109 #ifdef MAP_ANON
1110 PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANON) );
1111 PyDict_SetItemString (dict, "MAP_ANONYMOUS",
1112 PyInt_FromLong(MAP_ANON) );
1113 #endif
1115 PyDict_SetItemString (dict, "PAGESIZE",
1116 PyInt_FromLong( (long)my_getpagesize() ) );
1118 PyDict_SetItemString (dict, "ACCESS_READ",
1119 PyInt_FromLong(ACCESS_READ));
1120 PyDict_SetItemString (dict, "ACCESS_WRITE",
1121 PyInt_FromLong(ACCESS_WRITE));
1122 PyDict_SetItemString (dict, "ACCESS_COPY",
1123 PyInt_FromLong(ACCESS_COPY));