Updated for hfsplus module, new gusi libs.
[python/dscho.git] / Modules / mmapmodule.c
blob933e9722a3dba2bbfa09f92c21b537e0c52c8500
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_WIN32
24 #define UNIX
25 #endif
27 #ifdef MS_WIN32
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 <unistd.h>
40 #include <sys/mman.h>
41 #include <sys/stat.h>
43 #ifndef MS_SYNC
44 /* This is missing e.g. on SunOS 4.1.4 */
45 #define MS_SYNC 0
46 #endif
48 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
49 static int
50 my_getpagesize(void)
52 return sysconf(_SC_PAGESIZE);
54 #else
55 #define my_getpagesize getpagesize
56 #endif
58 #endif /* UNIX */
60 #include <string.h>
61 #include <sys/types.h>
63 static PyObject *mmap_module_error;
65 typedef enum
67 ACCESS_DEFAULT,
68 ACCESS_READ,
69 ACCESS_WRITE,
70 ACCESS_COPY
71 } access_mode;
73 typedef struct {
74 PyObject_HEAD
75 char * data;
76 size_t size;
77 size_t pos;
79 #ifdef MS_WIN32
80 HANDLE map_handle;
81 HANDLE file_handle;
82 char * tagname;
83 #endif
85 #ifdef UNIX
86 int fd;
87 #endif
89 access_mode access;
90 } mmap_object;
93 static void
94 mmap_object_dealloc(mmap_object *m_obj)
96 #ifdef MS_WIN32
97 if (m_obj->data != NULL)
98 UnmapViewOfFile (m_obj->data);
99 if (m_obj->map_handle != INVALID_HANDLE_VALUE)
100 CloseHandle (m_obj->map_handle);
101 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
102 CloseHandle (m_obj->file_handle);
103 if (m_obj->tagname)
104 PyMem_Free(m_obj->tagname);
105 #endif /* MS_WIN32 */
107 #ifdef UNIX
108 if (m_obj->data!=NULL) {
109 msync(m_obj->data, m_obj->size, MS_SYNC);
110 munmap(m_obj->data, m_obj->size);
112 #endif /* UNIX */
114 PyObject_Del(m_obj);
117 static PyObject *
118 mmap_close_method(mmap_object *self, PyObject *args)
120 if (!PyArg_ParseTuple(args, ":close"))
121 return NULL;
122 #ifdef MS_WIN32
123 /* For each resource we maintain, we need to check
124 the value is valid, and if so, free the resource
125 and set the member value to an invalid value so
126 the dealloc does not attempt to resource clearing
127 again.
128 TODO - should we check for errors in the close operations???
130 if (self->data != NULL) {
131 UnmapViewOfFile (self->data);
132 self->data = NULL;
134 if (self->map_handle != INVALID_HANDLE_VALUE) {
135 CloseHandle (self->map_handle);
136 self->map_handle = INVALID_HANDLE_VALUE;
138 if (self->file_handle != INVALID_HANDLE_VALUE) {
139 CloseHandle (self->file_handle);
140 self->file_handle = INVALID_HANDLE_VALUE;
142 #endif /* MS_WIN32 */
144 #ifdef UNIX
145 munmap(self->data, self->size);
146 self->data = NULL;
147 #endif
149 Py_INCREF (Py_None);
150 return (Py_None);
153 #ifdef MS_WIN32
154 #define CHECK_VALID(err) \
155 do { \
156 if (!self->map_handle) { \
157 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
158 return err; \
160 } while (0)
161 #endif /* MS_WIN32 */
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;
255 p = self->data + start;
257 while (p < e) {
258 char *s = p;
259 char *n = needle;
260 while ((s<e) && (*n) && !(*s-*n)) {
261 s++, n++;
263 if (!*n) {
264 return Py_BuildValue (
265 "l",
266 (long) (p - self->data));
268 p++;
270 return Py_BuildValue ("l", (long) -1);
274 static int
275 is_writeable(mmap_object *self)
277 if (self->access != ACCESS_READ)
278 return 1;
279 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
280 return 0;
283 static int
284 is_resizeable(mmap_object *self)
286 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
287 return 1;
288 PyErr_Format(PyExc_TypeError,
289 "mmap can't resize a readonly or copy-on-write memory map.");
290 return 0;
294 static PyObject *
295 mmap_write_method(mmap_object *self,
296 PyObject *args)
298 long length;
299 char *data;
301 CHECK_VALID(NULL);
302 if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
303 return(NULL);
305 if (!is_writeable(self))
306 return NULL;
308 if ((self->pos + length) > self->size) {
309 PyErr_SetString (PyExc_ValueError, "data out of range");
310 return NULL;
312 memcpy (self->data+self->pos, data, length);
313 self->pos = self->pos+length;
314 Py_INCREF (Py_None);
315 return (Py_None);
318 static PyObject *
319 mmap_write_byte_method(mmap_object *self,
320 PyObject *args)
322 char value;
324 CHECK_VALID(NULL);
325 if (!PyArg_ParseTuple (args, "c:write_byte", &value))
326 return(NULL);
328 if (!is_writeable(self))
329 return NULL;
330 *(self->data+self->pos) = value;
331 self->pos += 1;
332 Py_INCREF (Py_None);
333 return (Py_None);
336 static PyObject *
337 mmap_size_method(mmap_object *self,
338 PyObject *args)
340 CHECK_VALID(NULL);
341 if (!PyArg_ParseTuple(args, ":size"))
342 return NULL;
344 #ifdef MS_WIN32
345 if (self->file_handle != INVALID_HANDLE_VALUE) {
346 return (Py_BuildValue (
347 "l", (long)
348 GetFileSize (self->file_handle, NULL)));
349 } else {
350 return (Py_BuildValue ("l", (long) self->size) );
352 #endif /* MS_WIN32 */
354 #ifdef UNIX
356 struct stat buf;
357 if (-1 == fstat(self->fd, &buf)) {
358 PyErr_SetFromErrno(mmap_module_error);
359 return NULL;
361 return (Py_BuildValue ("l", (long) buf.st_size) );
363 #endif /* UNIX */
366 /* This assumes that you want the entire file mapped,
367 / and when recreating the map will make the new file
368 / have the new size
370 / Is this really necessary? This could easily be done
371 / from python by just closing and re-opening with the
372 / new size?
375 static PyObject *
376 mmap_resize_method(mmap_object *self,
377 PyObject *args)
379 unsigned long new_size;
380 CHECK_VALID(NULL);
381 if (!PyArg_ParseTuple (args, "l:resize", &new_size) ||
382 !is_resizeable(self)) {
383 return NULL;
384 #ifdef MS_WIN32
385 } else {
386 DWORD dwErrCode = 0;
387 /* First, unmap the file view */
388 UnmapViewOfFile (self->data);
389 /* Close the mapping object */
390 CloseHandle (self->map_handle);
391 /* Move to the desired EOF position */
392 SetFilePointer (self->file_handle,
393 new_size, NULL, FILE_BEGIN);
394 /* Change the size of the file */
395 SetEndOfFile (self->file_handle);
396 /* Create another mapping object and remap the file view */
397 self->map_handle = CreateFileMapping (
398 self->file_handle,
399 NULL,
400 PAGE_READWRITE,
402 new_size,
403 self->tagname);
404 if (self->map_handle != NULL) {
405 self->data = (char *) MapViewOfFile (self->map_handle,
406 FILE_MAP_WRITE,
410 if (self->data != NULL) {
411 self->size = new_size;
412 Py_INCREF (Py_None);
413 return Py_None;
414 } else {
415 dwErrCode = GetLastError();
417 } else {
418 dwErrCode = GetLastError();
420 PyErr_SetFromWindowsErr(dwErrCode);
421 return (NULL);
422 #endif /* MS_WIN32 */
424 #ifdef UNIX
425 #ifndef HAVE_MREMAP
426 } else {
427 PyErr_SetString(PyExc_SystemError,
428 "mmap: resizing not available--no mremap()");
429 return NULL;
430 #else
431 } else {
432 void *newmap;
434 #ifdef MREMAP_MAYMOVE
435 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
436 #else
437 newmap = mremap(self->data, self->size, new_size, 0);
438 #endif
439 if (newmap == (void *)-1)
441 PyErr_SetFromErrno(mmap_module_error);
442 return NULL;
444 self->data = newmap;
445 self->size = new_size;
446 Py_INCREF(Py_None);
447 return Py_None;
448 #endif /* HAVE_MREMAP */
449 #endif /* UNIX */
453 static PyObject *
454 mmap_tell_method(mmap_object *self, PyObject *args)
456 CHECK_VALID(NULL);
457 if (!PyArg_ParseTuple(args, ":tell"))
458 return NULL;
459 return (Py_BuildValue ("l", (long) self->pos) );
462 static PyObject *
463 mmap_flush_method(mmap_object *self, PyObject *args)
465 size_t offset = 0;
466 size_t size = self->size;
467 CHECK_VALID(NULL);
468 if (!PyArg_ParseTuple (args, "|ll:flush", &offset, &size)) {
469 return NULL;
470 } else if ((offset + size) > self->size) {
471 PyErr_SetString (PyExc_ValueError,
472 "flush values out of range");
473 return NULL;
474 } else {
475 #ifdef MS_WIN32
476 return (Py_BuildValue("l", (long)
477 FlushViewOfFile(self->data+offset, size)));
478 #endif /* MS_WIN32 */
479 #ifdef UNIX
480 /* XXX semantics of return value? */
481 /* XXX flags for msync? */
482 if (-1 == msync(self->data + offset, size,
483 MS_SYNC))
485 PyErr_SetFromErrno(mmap_module_error);
486 return NULL;
488 return Py_BuildValue ("l", (long) 0);
489 #endif /* UNIX */
493 static PyObject *
494 mmap_seek_method(mmap_object *self, PyObject *args)
496 int dist;
497 int how=0;
498 CHECK_VALID(NULL);
499 if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) {
500 return(NULL);
501 } else {
502 size_t where;
503 switch (how) {
504 case 0: /* relative to start */
505 if (dist < 0)
506 goto onoutofrange;
507 where = dist;
508 break;
509 case 1: /* relative to current position */
510 if ((int)self->pos + dist < 0)
511 goto onoutofrange;
512 where = self->pos + dist;
513 break;
514 case 2: /* relative to end */
515 if ((int)self->size + dist < 0)
516 goto onoutofrange;
517 where = self->size + dist;
518 break;
519 default:
520 PyErr_SetString (PyExc_ValueError,
521 "unknown seek type");
522 return NULL;
524 if (where > self->size)
525 goto onoutofrange;
526 self->pos = where;
527 Py_INCREF (Py_None);
528 return (Py_None);
531 onoutofrange:
532 PyErr_SetString (PyExc_ValueError, "seek out of range");
533 return NULL;
536 static PyObject *
537 mmap_move_method(mmap_object *self, PyObject *args)
539 unsigned long dest, src, count;
540 CHECK_VALID(NULL);
541 if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count) ||
542 !is_writeable(self)) {
543 return NULL;
544 } else {
545 /* bounds check the values */
546 if (/* end of source after end of data?? */
547 ((src+count) > self->size)
548 /* dest will fit? */
549 || (dest+count > self->size)) {
550 PyErr_SetString (PyExc_ValueError,
551 "source or destination out of range");
552 return NULL;
553 } else {
554 memmove (self->data+dest, self->data+src, count);
555 Py_INCREF (Py_None);
556 return Py_None;
561 static struct PyMethodDef mmap_object_methods[] = {
562 {"close", (PyCFunction) mmap_close_method, 1},
563 {"find", (PyCFunction) mmap_find_method, 1},
564 {"flush", (PyCFunction) mmap_flush_method, 1},
565 {"move", (PyCFunction) mmap_move_method, 1},
566 {"read", (PyCFunction) mmap_read_method, 1},
567 {"read_byte", (PyCFunction) mmap_read_byte_method, 1},
568 {"readline", (PyCFunction) mmap_read_line_method, 1},
569 {"resize", (PyCFunction) mmap_resize_method, 1},
570 {"seek", (PyCFunction) mmap_seek_method, 1},
571 {"size", (PyCFunction) mmap_size_method, 1},
572 {"tell", (PyCFunction) mmap_tell_method, 1},
573 {"write", (PyCFunction) mmap_write_method, 1},
574 {"write_byte", (PyCFunction) mmap_write_byte_method, 1},
575 {NULL, NULL} /* sentinel */
578 /* Functions for treating an mmap'ed file as a buffer */
580 static int
581 mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr)
583 CHECK_VALID(-1);
584 if ( index != 0 ) {
585 PyErr_SetString(PyExc_SystemError,
586 "Accessing non-existent mmap segment");
587 return -1;
589 *ptr = self->data;
590 return self->size;
593 static int
594 mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
596 CHECK_VALID(-1);
597 if ( index != 0 ) {
598 PyErr_SetString(PyExc_SystemError,
599 "Accessing non-existent mmap segment");
600 return -1;
602 if (!is_writeable(self))
603 return -1;
604 *ptr = self->data;
605 return self->size;
608 static int
609 mmap_buffer_getsegcount(mmap_object *self, int *lenp)
611 CHECK_VALID(-1);
612 if (lenp)
613 *lenp = self->size;
614 return 1;
617 static int
618 mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr)
620 if ( index != 0 ) {
621 PyErr_SetString(PyExc_SystemError,
622 "accessing non-existent buffer segment");
623 return -1;
625 *ptr = (const char *)self->data;
626 return self->size;
629 static PyObject *
630 mmap_object_getattr(mmap_object *self, char *name)
632 return Py_FindMethod (mmap_object_methods, (PyObject *)self, name);
635 static int
636 mmap_length(mmap_object *self)
638 CHECK_VALID(-1);
639 return self->size;
642 static PyObject *
643 mmap_item(mmap_object *self, int i)
645 CHECK_VALID(NULL);
646 if (i < 0 || (size_t)i >= self->size) {
647 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
648 return NULL;
650 return PyString_FromStringAndSize(self->data + i, 1);
653 static PyObject *
654 mmap_slice(mmap_object *self, int ilow, int ihigh)
656 CHECK_VALID(NULL);
657 if (ilow < 0)
658 ilow = 0;
659 else if ((size_t)ilow > self->size)
660 ilow = self->size;
661 if (ihigh < 0)
662 ihigh = 0;
663 if (ihigh < ilow)
664 ihigh = ilow;
665 else if ((size_t)ihigh > self->size)
666 ihigh = self->size;
668 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
671 static PyObject *
672 mmap_concat(mmap_object *self, PyObject *bb)
674 CHECK_VALID(NULL);
675 PyErr_SetString(PyExc_SystemError,
676 "mmaps don't support concatenation");
677 return NULL;
680 static PyObject *
681 mmap_repeat(mmap_object *self, int n)
683 CHECK_VALID(NULL);
684 PyErr_SetString(PyExc_SystemError,
685 "mmaps don't support repeat operation");
686 return NULL;
689 static int
690 mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
692 const char *buf;
694 CHECK_VALID(-1);
695 if (ilow < 0)
696 ilow = 0;
697 else if ((size_t)ilow > self->size)
698 ilow = self->size;
699 if (ihigh < 0)
700 ihigh = 0;
701 if (ihigh < ilow)
702 ihigh = ilow;
703 else if ((size_t)ihigh > self->size)
704 ihigh = self->size;
706 if (v == NULL) {
707 PyErr_SetString(PyExc_TypeError,
708 "mmap object doesn't support slice deletion");
709 return -1;
711 if (! (PyString_Check(v)) ) {
712 PyErr_SetString(PyExc_IndexError,
713 "mmap slice assignment must be a string");
714 return -1;
716 if ( PyString_Size(v) != (ihigh - ilow) ) {
717 PyErr_SetString(PyExc_IndexError,
718 "mmap slice assignment is wrong size");
719 return -1;
721 if (!is_writeable(self))
722 return -1;
723 buf = PyString_AsString(v);
724 memcpy(self->data + ilow, buf, ihigh-ilow);
725 return 0;
728 static int
729 mmap_ass_item(mmap_object *self, int i, PyObject *v)
731 const char *buf;
733 CHECK_VALID(-1);
734 if (i < 0 || (size_t)i >= self->size) {
735 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
736 return -1;
738 if (v == NULL) {
739 PyErr_SetString(PyExc_TypeError,
740 "mmap object doesn't support item deletion");
741 return -1;
743 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
744 PyErr_SetString(PyExc_IndexError,
745 "mmap assignment must be single-character string");
746 return -1;
748 if (!is_writeable(self))
749 return -1;
750 buf = PyString_AsString(v);
751 self->data[i] = buf[0];
752 return 0;
755 static PySequenceMethods mmap_as_sequence = {
756 (inquiry)mmap_length, /*sq_length*/
757 (binaryfunc)mmap_concat, /*sq_concat*/
758 (intargfunc)mmap_repeat, /*sq_repeat*/
759 (intargfunc)mmap_item, /*sq_item*/
760 (intintargfunc)mmap_slice, /*sq_slice*/
761 (intobjargproc)mmap_ass_item, /*sq_ass_item*/
762 (intintobjargproc)mmap_ass_slice, /*sq_ass_slice*/
765 static PyBufferProcs mmap_as_buffer = {
766 (getreadbufferproc)mmap_buffer_getreadbuf,
767 (getwritebufferproc)mmap_buffer_getwritebuf,
768 (getsegcountproc)mmap_buffer_getsegcount,
769 (getcharbufferproc)mmap_buffer_getcharbuffer,
772 static PyTypeObject mmap_object_type = {
773 PyObject_HEAD_INIT(0) /* patched in module init */
774 0, /* ob_size */
775 "mmap.mmap", /* tp_name */
776 sizeof(mmap_object), /* tp_size */
777 0, /* tp_itemsize */
778 /* methods */
779 (destructor) mmap_object_dealloc, /* tp_dealloc */
780 0, /* tp_print */
781 (getattrfunc) mmap_object_getattr, /* tp_getattr */
782 0, /* tp_setattr */
783 0, /* tp_compare */
784 0, /* tp_repr */
785 0, /* tp_as_number */
786 &mmap_as_sequence, /*tp_as_sequence*/
787 0, /*tp_as_mapping*/
788 0, /*tp_hash*/
789 0, /*tp_call*/
790 0, /*tp_str*/
791 0, /*tp_getattro*/
792 0, /*tp_setattro*/
793 &mmap_as_buffer, /*tp_as_buffer*/
794 Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
795 0, /*tp_doc*/
799 /* extract the map size from the given PyObject
801 The map size is restricted to [0, INT_MAX] because this is the current
802 Python limitation on object sizes. Although the mmap object *could* handle
803 a larger map size, there is no point because all the useful operations
804 (len(), slicing(), sequence indexing) are limited by a C int.
806 Returns -1 on error, with an appropriate Python exception raised. On
807 success, the map size is returned. */
808 static int
809 _GetMapSize(PyObject *o)
811 if (PyInt_Check(o)) {
812 long i = PyInt_AsLong(o);
813 if (PyErr_Occurred())
814 return -1;
815 if (i < 0)
816 goto onnegoverflow;
817 if (i > INT_MAX)
818 goto onposoverflow;
819 return (int)i;
821 else if (PyLong_Check(o)) {
822 long i = PyLong_AsLong(o);
823 if (PyErr_Occurred()) {
824 /* yes negative overflow is mistaken for positive overflow
825 but not worth the trouble to check sign of 'i' */
826 if (PyErr_ExceptionMatches(PyExc_OverflowError))
827 goto onposoverflow;
828 else
829 return -1;
831 if (i < 0)
832 goto onnegoverflow;
833 if (i > INT_MAX)
834 goto onposoverflow;
835 return (int)i;
837 else {
838 PyErr_SetString(PyExc_TypeError,
839 "map size must be an integral value");
840 return -1;
843 onnegoverflow:
844 PyErr_SetString(PyExc_OverflowError,
845 "memory mapped size must be positive");
846 return -1;
848 onposoverflow:
849 PyErr_SetString(PyExc_OverflowError,
850 "memory mapped size is too large (limited by C int)");
851 return -1;
854 #ifdef UNIX
855 static PyObject *
856 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
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 m_obj = PyObject_New (mmap_object, &mmap_object_type);
900 if (m_obj == NULL) {return NULL;}
901 m_obj->size = (size_t) map_size;
902 m_obj->pos = (size_t) 0;
903 m_obj->fd = fd;
904 m_obj->data = mmap(NULL, map_size,
905 prot, flags,
906 fd, 0);
907 if (m_obj->data == (char *)-1) {
908 Py_DECREF(m_obj);
909 PyErr_SetFromErrno(mmap_module_error);
910 return NULL;
912 m_obj->access = access;
913 return (PyObject *)m_obj;
915 #endif /* UNIX */
917 #ifdef MS_WIN32
918 static PyObject *
919 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
921 mmap_object *m_obj;
922 PyObject *map_size_obj = NULL;
923 int map_size;
924 char *tagname = "";
925 DWORD dwErr = 0;
926 int fileno;
927 HANDLE fh = 0;
928 access_mode access = ACCESS_DEFAULT;
929 DWORD flProtect, dwDesiredAccess;
930 char *keywords[] = { "fileno", "length",
931 "tagname",
932 "access", NULL };
934 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
935 &fileno, &map_size_obj,
936 &tagname, &access)) {
937 return NULL;
940 switch(access) {
941 case ACCESS_READ:
942 flProtect = PAGE_READONLY;
943 dwDesiredAccess = FILE_MAP_READ;
944 break;
945 case ACCESS_DEFAULT: case ACCESS_WRITE:
946 flProtect = PAGE_READWRITE;
947 dwDesiredAccess = FILE_MAP_WRITE;
948 break;
949 case ACCESS_COPY:
950 flProtect = PAGE_WRITECOPY;
951 dwDesiredAccess = FILE_MAP_COPY;
952 break;
953 default:
954 return PyErr_Format(PyExc_ValueError,
955 "mmap invalid access parameter.");
958 map_size = _GetMapSize(map_size_obj);
959 if (map_size < 0)
960 return NULL;
962 /* if an actual filename has been specified */
963 if (fileno != 0) {
964 fh = (HANDLE)_get_osfhandle(fileno);
965 if (fh==(HANDLE)-1) {
966 PyErr_SetFromErrno(mmap_module_error);
967 return NULL;
969 /* Win9x appears to need us seeked to zero */
970 fseek(&_iob[fileno], 0, SEEK_SET);
973 m_obj = PyObject_New (mmap_object, &mmap_object_type);
974 if (m_obj==NULL)
975 return NULL;
976 /* Set every field to an invalid marker, so we can safely
977 destruct the object in the face of failure */
978 m_obj->data = NULL;
979 m_obj->file_handle = INVALID_HANDLE_VALUE;
980 m_obj->map_handle = INVALID_HANDLE_VALUE;
981 m_obj->tagname = NULL;
983 if (fh) {
984 /* It is necessary to duplicate the handle, so the
985 Python code can close it on us */
986 if (!DuplicateHandle(
987 GetCurrentProcess(), /* source process handle */
988 fh, /* handle to be duplicated */
989 GetCurrentProcess(), /* target proc handle */
990 (LPHANDLE)&m_obj->file_handle, /* result */
991 0, /* access - ignored due to options value */
992 FALSE, /* inherited by child processes? */
993 DUPLICATE_SAME_ACCESS)) { /* options */
994 dwErr = GetLastError();
995 Py_DECREF(m_obj);
996 PyErr_SetFromWindowsErr(dwErr);
997 return NULL;
999 if (!map_size) {
1000 m_obj->size = GetFileSize (fh, NULL);
1001 } else {
1002 m_obj->size = map_size;
1005 else {
1006 m_obj->size = map_size;
1009 /* set the initial position */
1010 m_obj->pos = (size_t) 0;
1012 /* set the tag name */
1013 if (tagname != NULL && *tagname != '\0') {
1014 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1015 if (m_obj->tagname == NULL) {
1016 PyErr_NoMemory();
1017 Py_DECREF(m_obj);
1018 return NULL;
1020 strcpy(m_obj->tagname, tagname);
1022 else
1023 m_obj->tagname = NULL;
1025 m_obj->access = access;
1026 m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
1027 NULL,
1028 flProtect,
1030 m_obj->size,
1031 m_obj->tagname);
1032 if (m_obj->map_handle != NULL) {
1033 m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
1034 dwDesiredAccess,
1038 if (m_obj->data != NULL) {
1039 return ((PyObject *) m_obj);
1040 } else {
1041 dwErr = GetLastError();
1043 } else {
1044 dwErr = GetLastError();
1046 Py_DECREF(m_obj);
1047 PyErr_SetFromWindowsErr(dwErr);
1048 return (NULL);
1050 #endif /* MS_WIN32 */
1052 /* List of functions exported by this module */
1053 static struct PyMethodDef mmap_functions[] = {
1054 {"mmap", (PyCFunction) new_mmap_object,
1055 METH_VARARGS|METH_KEYWORDS},
1056 {NULL, NULL} /* Sentinel */
1059 DL_EXPORT(void)
1060 initmmap(void)
1062 PyObject *dict, *module;
1064 /* Patch the object type */
1065 mmap_object_type.ob_type = &PyType_Type;
1067 module = Py_InitModule ("mmap", mmap_functions);
1068 dict = PyModule_GetDict (module);
1069 mmap_module_error = PyExc_EnvironmentError;
1070 Py_INCREF(mmap_module_error);
1071 PyDict_SetItemString (dict, "error", mmap_module_error);
1072 #ifdef PROT_EXEC
1073 PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) );
1074 #endif
1075 #ifdef PROT_READ
1076 PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) );
1077 #endif
1078 #ifdef PROT_WRITE
1079 PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) );
1080 #endif
1082 #ifdef MAP_SHARED
1083 PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) );
1084 #endif
1085 #ifdef MAP_PRIVATE
1086 PyDict_SetItemString (dict, "MAP_PRIVATE",
1087 PyInt_FromLong(MAP_PRIVATE) );
1088 #endif
1089 #ifdef MAP_DENYWRITE
1090 PyDict_SetItemString (dict, "MAP_DENYWRITE",
1091 PyInt_FromLong(MAP_DENYWRITE) );
1092 #endif
1093 #ifdef MAP_EXECUTABLE
1094 PyDict_SetItemString (dict, "MAP_EXECUTABLE",
1095 PyInt_FromLong(MAP_EXECUTABLE) );
1096 #endif
1097 #ifdef MAP_ANON
1098 PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANON) );
1099 PyDict_SetItemString (dict, "MAP_ANONYMOUS",
1100 PyInt_FromLong(MAP_ANON) );
1101 #endif
1103 PyDict_SetItemString (dict, "PAGESIZE",
1104 PyInt_FromLong( (long)my_getpagesize() ) );
1106 PyDict_SetItemString (dict, "ACCESS_READ",
1107 PyInt_FromLong(ACCESS_READ));
1108 PyDict_SetItemString (dict, "ACCESS_WRITE",
1109 PyInt_FromLong(ACCESS_WRITE));
1110 PyDict_SetItemString (dict, "ACCESS_COPY",
1111 PyInt_FromLong(ACCESS_COPY));