- Got rid of newmodule.c
[python/dscho.git] / Modules / mmapmodule.c
blob3164a274076a71310c3079a11c298f2a2461bb87
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 <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_WIN32
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_WIN32
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_WIN32 */
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_WIN32
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_WIN32 */
143 #ifdef UNIX
144 munmap(self->data, self->size);
145 self->data = NULL;
146 #endif
148 Py_INCREF (Py_None);
149 return (Py_None);
152 #ifdef MS_WIN32
153 #define CHECK_VALID(err) \
154 do { \
155 if (!self->map_handle) { \
156 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
157 return err; \
159 } while (0)
160 #endif /* MS_WIN32 */
162 #ifdef UNIX
163 #define CHECK_VALID(err) \
164 do { \
165 if (self->data == NULL) { \
166 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
167 return err; \
169 } while (0)
170 #endif /* UNIX */
172 static PyObject *
173 mmap_read_byte_method(mmap_object *self,
174 PyObject *args)
176 CHECK_VALID(NULL);
177 if (!PyArg_ParseTuple(args, ":read_byte"))
178 return NULL;
179 if (self->pos < self->size) {
180 char value = self->data[self->pos];
181 self->pos += 1;
182 return Py_BuildValue("c", value);
183 } else {
184 PyErr_SetString (PyExc_ValueError, "read byte out of range");
185 return NULL;
189 static PyObject *
190 mmap_read_line_method(mmap_object *self,
191 PyObject *args)
193 char *start = self->data+self->pos;
194 char *eof = self->data+self->size;
195 char *eol;
196 PyObject *result;
198 CHECK_VALID(NULL);
199 if (!PyArg_ParseTuple(args, ":readline"))
200 return NULL;
202 eol = memchr(start, '\n', self->size - self->pos);
203 if (!eol)
204 eol = eof;
205 else
206 ++eol; /* we're interested in the position after the
207 newline. */
208 result = PyString_FromStringAndSize(start, (eol - start));
209 self->pos += (eol - start);
210 return (result);
213 static PyObject *
214 mmap_read_method(mmap_object *self,
215 PyObject *args)
217 long num_bytes;
218 PyObject *result;
220 CHECK_VALID(NULL);
221 if (!PyArg_ParseTuple(args, "l:read", &num_bytes))
222 return(NULL);
224 /* silently 'adjust' out-of-range requests */
225 if ((self->pos + num_bytes) > self->size) {
226 num_bytes -= (self->pos+num_bytes) - self->size;
228 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
229 self->pos += num_bytes;
230 return (result);
233 static PyObject *
234 mmap_find_method(mmap_object *self,
235 PyObject *args)
237 long start = self->pos;
238 char *needle;
239 int len;
241 CHECK_VALID(NULL);
242 if (!PyArg_ParseTuple (args, "s#|l:find", &needle, &len, &start)) {
243 return NULL;
244 } else {
245 char *p;
246 char *e = self->data + self->size;
248 if (start < 0)
249 start += self->size;
250 if (start < 0)
251 start = 0;
252 else if ((size_t)start > self->size)
253 start = self->size;
255 for (p = self->data + start; p + len <= e; ++p) {
256 int i;
257 for (i = 0; i < len && needle[i] == p[i]; ++i)
258 /* nothing */;
259 if (i == len) {
260 return Py_BuildValue (
261 "l",
262 (long) (p - self->data));
265 return Py_BuildValue ("l", (long) -1);
269 static int
270 is_writeable(mmap_object *self)
272 if (self->access != ACCESS_READ)
273 return 1;
274 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
275 return 0;
278 static int
279 is_resizeable(mmap_object *self)
281 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
282 return 1;
283 PyErr_Format(PyExc_TypeError,
284 "mmap can't resize a readonly or copy-on-write memory map.");
285 return 0;
289 static PyObject *
290 mmap_write_method(mmap_object *self,
291 PyObject *args)
293 long length;
294 char *data;
296 CHECK_VALID(NULL);
297 if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
298 return(NULL);
300 if (!is_writeable(self))
301 return NULL;
303 if ((self->pos + length) > self->size) {
304 PyErr_SetString (PyExc_ValueError, "data out of range");
305 return NULL;
307 memcpy (self->data+self->pos, data, length);
308 self->pos = self->pos+length;
309 Py_INCREF (Py_None);
310 return (Py_None);
313 static PyObject *
314 mmap_write_byte_method(mmap_object *self,
315 PyObject *args)
317 char value;
319 CHECK_VALID(NULL);
320 if (!PyArg_ParseTuple (args, "c:write_byte", &value))
321 return(NULL);
323 if (!is_writeable(self))
324 return NULL;
325 *(self->data+self->pos) = value;
326 self->pos += 1;
327 Py_INCREF (Py_None);
328 return (Py_None);
331 static PyObject *
332 mmap_size_method(mmap_object *self,
333 PyObject *args)
335 CHECK_VALID(NULL);
336 if (!PyArg_ParseTuple(args, ":size"))
337 return NULL;
339 #ifdef MS_WIN32
340 if (self->file_handle != INVALID_HANDLE_VALUE) {
341 return (Py_BuildValue (
342 "l", (long)
343 GetFileSize (self->file_handle, NULL)));
344 } else {
345 return (Py_BuildValue ("l", (long) self->size) );
347 #endif /* MS_WIN32 */
349 #ifdef UNIX
351 struct stat buf;
352 if (-1 == fstat(self->fd, &buf)) {
353 PyErr_SetFromErrno(mmap_module_error);
354 return NULL;
356 return (Py_BuildValue ("l", (long) buf.st_size) );
358 #endif /* UNIX */
361 /* This assumes that you want the entire file mapped,
362 / and when recreating the map will make the new file
363 / have the new size
365 / Is this really necessary? This could easily be done
366 / from python by just closing and re-opening with the
367 / new size?
370 static PyObject *
371 mmap_resize_method(mmap_object *self,
372 PyObject *args)
374 unsigned long new_size;
375 CHECK_VALID(NULL);
376 if (!PyArg_ParseTuple (args, "l:resize", &new_size) ||
377 !is_resizeable(self)) {
378 return NULL;
379 #ifdef MS_WIN32
380 } else {
381 DWORD dwErrCode = 0;
382 /* First, unmap the file view */
383 UnmapViewOfFile (self->data);
384 /* Close the mapping object */
385 CloseHandle (self->map_handle);
386 /* Move to the desired EOF position */
387 SetFilePointer (self->file_handle,
388 new_size, NULL, FILE_BEGIN);
389 /* Change the size of the file */
390 SetEndOfFile (self->file_handle);
391 /* Create another mapping object and remap the file view */
392 self->map_handle = CreateFileMapping (
393 self->file_handle,
394 NULL,
395 PAGE_READWRITE,
397 new_size,
398 self->tagname);
399 if (self->map_handle != NULL) {
400 self->data = (char *) MapViewOfFile (self->map_handle,
401 FILE_MAP_WRITE,
405 if (self->data != NULL) {
406 self->size = new_size;
407 Py_INCREF (Py_None);
408 return Py_None;
409 } else {
410 dwErrCode = GetLastError();
412 } else {
413 dwErrCode = GetLastError();
415 PyErr_SetFromWindowsErr(dwErrCode);
416 return (NULL);
417 #endif /* MS_WIN32 */
419 #ifdef UNIX
420 #ifndef HAVE_MREMAP
421 } else {
422 PyErr_SetString(PyExc_SystemError,
423 "mmap: resizing not available--no mremap()");
424 return NULL;
425 #else
426 } else {
427 void *newmap;
429 #ifdef MREMAP_MAYMOVE
430 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
431 #else
432 newmap = mremap(self->data, self->size, new_size, 0);
433 #endif
434 if (newmap == (void *)-1)
436 PyErr_SetFromErrno(mmap_module_error);
437 return NULL;
439 self->data = newmap;
440 self->size = new_size;
441 Py_INCREF(Py_None);
442 return Py_None;
443 #endif /* HAVE_MREMAP */
444 #endif /* UNIX */
448 static PyObject *
449 mmap_tell_method(mmap_object *self, PyObject *args)
451 CHECK_VALID(NULL);
452 if (!PyArg_ParseTuple(args, ":tell"))
453 return NULL;
454 return (Py_BuildValue ("l", (long) self->pos) );
457 static PyObject *
458 mmap_flush_method(mmap_object *self, PyObject *args)
460 size_t offset = 0;
461 size_t size = self->size;
462 CHECK_VALID(NULL);
463 if (!PyArg_ParseTuple (args, "|ll:flush", &offset, &size)) {
464 return NULL;
465 } else if ((offset + size) > self->size) {
466 PyErr_SetString (PyExc_ValueError,
467 "flush values out of range");
468 return NULL;
469 } else {
470 #ifdef MS_WIN32
471 return (Py_BuildValue("l", (long)
472 FlushViewOfFile(self->data+offset, size)));
473 #endif /* MS_WIN32 */
474 #ifdef UNIX
475 /* XXX semantics of return value? */
476 /* XXX flags for msync? */
477 if (-1 == msync(self->data + offset, size,
478 MS_SYNC))
480 PyErr_SetFromErrno(mmap_module_error);
481 return NULL;
483 return Py_BuildValue ("l", (long) 0);
484 #endif /* UNIX */
488 static PyObject *
489 mmap_seek_method(mmap_object *self, PyObject *args)
491 int dist;
492 int how=0;
493 CHECK_VALID(NULL);
494 if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) {
495 return(NULL);
496 } else {
497 size_t where;
498 switch (how) {
499 case 0: /* relative to start */
500 if (dist < 0)
501 goto onoutofrange;
502 where = dist;
503 break;
504 case 1: /* relative to current position */
505 if ((int)self->pos + dist < 0)
506 goto onoutofrange;
507 where = self->pos + dist;
508 break;
509 case 2: /* relative to end */
510 if ((int)self->size + dist < 0)
511 goto onoutofrange;
512 where = self->size + dist;
513 break;
514 default:
515 PyErr_SetString (PyExc_ValueError,
516 "unknown seek type");
517 return NULL;
519 if (where > self->size)
520 goto onoutofrange;
521 self->pos = where;
522 Py_INCREF (Py_None);
523 return (Py_None);
526 onoutofrange:
527 PyErr_SetString (PyExc_ValueError, "seek out of range");
528 return NULL;
531 static PyObject *
532 mmap_move_method(mmap_object *self, PyObject *args)
534 unsigned long dest, src, count;
535 CHECK_VALID(NULL);
536 if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count) ||
537 !is_writeable(self)) {
538 return NULL;
539 } else {
540 /* bounds check the values */
541 if (/* end of source after end of data?? */
542 ((src+count) > self->size)
543 /* dest will fit? */
544 || (dest+count > self->size)) {
545 PyErr_SetString (PyExc_ValueError,
546 "source or destination out of range");
547 return NULL;
548 } else {
549 memmove (self->data+dest, self->data+src, count);
550 Py_INCREF (Py_None);
551 return Py_None;
556 static struct PyMethodDef mmap_object_methods[] = {
557 {"close", (PyCFunction) mmap_close_method, METH_VARARGS},
558 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
559 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
560 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
561 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
562 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_VARARGS},
563 {"readline", (PyCFunction) mmap_read_line_method, METH_VARARGS},
564 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
565 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
566 {"size", (PyCFunction) mmap_size_method, METH_VARARGS},
567 {"tell", (PyCFunction) mmap_tell_method, METH_VARARGS},
568 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
569 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
570 {NULL, NULL} /* sentinel */
573 /* Functions for treating an mmap'ed file as a buffer */
575 static int
576 mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr)
578 CHECK_VALID(-1);
579 if ( index != 0 ) {
580 PyErr_SetString(PyExc_SystemError,
581 "Accessing non-existent mmap segment");
582 return -1;
584 *ptr = self->data;
585 return self->size;
588 static int
589 mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
591 CHECK_VALID(-1);
592 if ( index != 0 ) {
593 PyErr_SetString(PyExc_SystemError,
594 "Accessing non-existent mmap segment");
595 return -1;
597 if (!is_writeable(self))
598 return -1;
599 *ptr = self->data;
600 return self->size;
603 static int
604 mmap_buffer_getsegcount(mmap_object *self, int *lenp)
606 CHECK_VALID(-1);
607 if (lenp)
608 *lenp = self->size;
609 return 1;
612 static int
613 mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr)
615 if ( index != 0 ) {
616 PyErr_SetString(PyExc_SystemError,
617 "accessing non-existent buffer segment");
618 return -1;
620 *ptr = (const char *)self->data;
621 return self->size;
624 static PyObject *
625 mmap_object_getattr(mmap_object *self, char *name)
627 return Py_FindMethod (mmap_object_methods, (PyObject *)self, name);
630 static int
631 mmap_length(mmap_object *self)
633 CHECK_VALID(-1);
634 return self->size;
637 static PyObject *
638 mmap_item(mmap_object *self, int i)
640 CHECK_VALID(NULL);
641 if (i < 0 || (size_t)i >= self->size) {
642 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
643 return NULL;
645 return PyString_FromStringAndSize(self->data + i, 1);
648 static PyObject *
649 mmap_slice(mmap_object *self, int ilow, int ihigh)
651 CHECK_VALID(NULL);
652 if (ilow < 0)
653 ilow = 0;
654 else if ((size_t)ilow > self->size)
655 ilow = self->size;
656 if (ihigh < 0)
657 ihigh = 0;
658 if (ihigh < ilow)
659 ihigh = ilow;
660 else if ((size_t)ihigh > self->size)
661 ihigh = self->size;
663 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
666 static PyObject *
667 mmap_concat(mmap_object *self, PyObject *bb)
669 CHECK_VALID(NULL);
670 PyErr_SetString(PyExc_SystemError,
671 "mmaps don't support concatenation");
672 return NULL;
675 static PyObject *
676 mmap_repeat(mmap_object *self, int n)
678 CHECK_VALID(NULL);
679 PyErr_SetString(PyExc_SystemError,
680 "mmaps don't support repeat operation");
681 return NULL;
684 static int
685 mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
687 const char *buf;
689 CHECK_VALID(-1);
690 if (ilow < 0)
691 ilow = 0;
692 else if ((size_t)ilow > self->size)
693 ilow = self->size;
694 if (ihigh < 0)
695 ihigh = 0;
696 if (ihigh < ilow)
697 ihigh = ilow;
698 else if ((size_t)ihigh > self->size)
699 ihigh = self->size;
701 if (v == NULL) {
702 PyErr_SetString(PyExc_TypeError,
703 "mmap object doesn't support slice deletion");
704 return -1;
706 if (! (PyString_Check(v)) ) {
707 PyErr_SetString(PyExc_IndexError,
708 "mmap slice assignment must be a string");
709 return -1;
711 if ( PyString_Size(v) != (ihigh - ilow) ) {
712 PyErr_SetString(PyExc_IndexError,
713 "mmap slice assignment is wrong size");
714 return -1;
716 if (!is_writeable(self))
717 return -1;
718 buf = PyString_AsString(v);
719 memcpy(self->data + ilow, buf, ihigh-ilow);
720 return 0;
723 static int
724 mmap_ass_item(mmap_object *self, int i, PyObject *v)
726 const char *buf;
728 CHECK_VALID(-1);
729 if (i < 0 || (size_t)i >= self->size) {
730 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
731 return -1;
733 if (v == NULL) {
734 PyErr_SetString(PyExc_TypeError,
735 "mmap object doesn't support item deletion");
736 return -1;
738 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
739 PyErr_SetString(PyExc_IndexError,
740 "mmap assignment must be single-character string");
741 return -1;
743 if (!is_writeable(self))
744 return -1;
745 buf = PyString_AsString(v);
746 self->data[i] = buf[0];
747 return 0;
750 static PySequenceMethods mmap_as_sequence = {
751 (inquiry)mmap_length, /*sq_length*/
752 (binaryfunc)mmap_concat, /*sq_concat*/
753 (intargfunc)mmap_repeat, /*sq_repeat*/
754 (intargfunc)mmap_item, /*sq_item*/
755 (intintargfunc)mmap_slice, /*sq_slice*/
756 (intobjargproc)mmap_ass_item, /*sq_ass_item*/
757 (intintobjargproc)mmap_ass_slice, /*sq_ass_slice*/
760 static PyBufferProcs mmap_as_buffer = {
761 (getreadbufferproc)mmap_buffer_getreadbuf,
762 (getwritebufferproc)mmap_buffer_getwritebuf,
763 (getsegcountproc)mmap_buffer_getsegcount,
764 (getcharbufferproc)mmap_buffer_getcharbuffer,
767 static PyTypeObject mmap_object_type = {
768 PyObject_HEAD_INIT(0) /* patched in module init */
769 0, /* ob_size */
770 "mmap.mmap", /* tp_name */
771 sizeof(mmap_object), /* tp_size */
772 0, /* tp_itemsize */
773 /* methods */
774 (destructor) mmap_object_dealloc, /* tp_dealloc */
775 0, /* tp_print */
776 (getattrfunc) mmap_object_getattr, /* tp_getattr */
777 0, /* tp_setattr */
778 0, /* tp_compare */
779 0, /* tp_repr */
780 0, /* tp_as_number */
781 &mmap_as_sequence, /*tp_as_sequence*/
782 0, /*tp_as_mapping*/
783 0, /*tp_hash*/
784 0, /*tp_call*/
785 0, /*tp_str*/
786 0, /*tp_getattro*/
787 0, /*tp_setattro*/
788 &mmap_as_buffer, /*tp_as_buffer*/
789 Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
790 0, /*tp_doc*/
794 /* extract the map size from the given PyObject
796 The map size is restricted to [0, INT_MAX] because this is the current
797 Python limitation on object sizes. Although the mmap object *could* handle
798 a larger map size, there is no point because all the useful operations
799 (len(), slicing(), sequence indexing) are limited by a C int.
801 Returns -1 on error, with an appropriate Python exception raised. On
802 success, the map size is returned. */
803 static int
804 _GetMapSize(PyObject *o)
806 if (PyInt_Check(o)) {
807 long i = PyInt_AsLong(o);
808 if (PyErr_Occurred())
809 return -1;
810 if (i < 0)
811 goto onnegoverflow;
812 if (i > INT_MAX)
813 goto onposoverflow;
814 return (int)i;
816 else if (PyLong_Check(o)) {
817 long i = PyLong_AsLong(o);
818 if (PyErr_Occurred()) {
819 /* yes negative overflow is mistaken for positive overflow
820 but not worth the trouble to check sign of 'i' */
821 if (PyErr_ExceptionMatches(PyExc_OverflowError))
822 goto onposoverflow;
823 else
824 return -1;
826 if (i < 0)
827 goto onnegoverflow;
828 if (i > INT_MAX)
829 goto onposoverflow;
830 return (int)i;
832 else {
833 PyErr_SetString(PyExc_TypeError,
834 "map size must be an integral value");
835 return -1;
838 onnegoverflow:
839 PyErr_SetString(PyExc_OverflowError,
840 "memory mapped size must be positive");
841 return -1;
843 onposoverflow:
844 PyErr_SetString(PyExc_OverflowError,
845 "memory mapped size is too large (limited by C int)");
846 return -1;
849 #ifdef UNIX
850 static PyObject *
851 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
853 mmap_object *m_obj;
854 PyObject *map_size_obj = NULL;
855 int map_size;
856 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
857 access_mode access = ACCESS_DEFAULT;
858 char *keywords[] = {"fileno", "length",
859 "flags", "prot",
860 "access", NULL};
862 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords,
863 &fd, &map_size_obj, &flags, &prot, &access))
864 return NULL;
865 map_size = _GetMapSize(map_size_obj);
866 if (map_size < 0)
867 return NULL;
869 if ((access != ACCESS_DEFAULT) &&
870 ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ))))
871 return PyErr_Format(PyExc_ValueError,
872 "mmap can't specify both access and flags, prot.");
873 switch(access) {
874 case ACCESS_READ:
875 flags = MAP_SHARED;
876 prot = PROT_READ;
877 break;
878 case ACCESS_WRITE:
879 flags = MAP_SHARED;
880 prot = PROT_READ | PROT_WRITE;
881 break;
882 case ACCESS_COPY:
883 flags = MAP_PRIVATE;
884 prot = PROT_READ | PROT_WRITE;
885 break;
886 case ACCESS_DEFAULT:
887 /* use the specified or default values of flags and prot */
888 break;
889 default:
890 return PyErr_Format(PyExc_ValueError,
891 "mmap invalid access parameter.");
894 m_obj = PyObject_New (mmap_object, &mmap_object_type);
895 if (m_obj == NULL) {return NULL;}
896 m_obj->size = (size_t) map_size;
897 m_obj->pos = (size_t) 0;
898 m_obj->fd = fd;
899 m_obj->data = mmap(NULL, map_size,
900 prot, flags,
901 fd, 0);
902 if (m_obj->data == (char *)-1) {
903 Py_DECREF(m_obj);
904 PyErr_SetFromErrno(mmap_module_error);
905 return NULL;
907 m_obj->access = access;
908 return (PyObject *)m_obj;
910 #endif /* UNIX */
912 #ifdef MS_WIN32
913 static PyObject *
914 new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
916 mmap_object *m_obj;
917 PyObject *map_size_obj = NULL;
918 int map_size;
919 char *tagname = "";
920 DWORD dwErr = 0;
921 int fileno;
922 HANDLE fh = 0;
923 access_mode access = ACCESS_DEFAULT;
924 DWORD flProtect, dwDesiredAccess;
925 char *keywords[] = { "fileno", "length",
926 "tagname",
927 "access", NULL };
929 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
930 &fileno, &map_size_obj,
931 &tagname, &access)) {
932 return NULL;
935 switch(access) {
936 case ACCESS_READ:
937 flProtect = PAGE_READONLY;
938 dwDesiredAccess = FILE_MAP_READ;
939 break;
940 case ACCESS_DEFAULT: case ACCESS_WRITE:
941 flProtect = PAGE_READWRITE;
942 dwDesiredAccess = FILE_MAP_WRITE;
943 break;
944 case ACCESS_COPY:
945 flProtect = PAGE_WRITECOPY;
946 dwDesiredAccess = FILE_MAP_COPY;
947 break;
948 default:
949 return PyErr_Format(PyExc_ValueError,
950 "mmap invalid access parameter.");
953 map_size = _GetMapSize(map_size_obj);
954 if (map_size < 0)
955 return NULL;
957 /* if an actual filename has been specified */
958 if (fileno != 0) {
959 fh = (HANDLE)_get_osfhandle(fileno);
960 if (fh==(HANDLE)-1) {
961 PyErr_SetFromErrno(mmap_module_error);
962 return NULL;
964 /* Win9x appears to need us seeked to zero */
965 fseek(&_iob[fileno], 0, SEEK_SET);
968 m_obj = PyObject_New (mmap_object, &mmap_object_type);
969 if (m_obj==NULL)
970 return NULL;
971 /* Set every field to an invalid marker, so we can safely
972 destruct the object in the face of failure */
973 m_obj->data = NULL;
974 m_obj->file_handle = INVALID_HANDLE_VALUE;
975 m_obj->map_handle = INVALID_HANDLE_VALUE;
976 m_obj->tagname = NULL;
978 if (fh) {
979 /* It is necessary to duplicate the handle, so the
980 Python code can close it on us */
981 if (!DuplicateHandle(
982 GetCurrentProcess(), /* source process handle */
983 fh, /* handle to be duplicated */
984 GetCurrentProcess(), /* target proc handle */
985 (LPHANDLE)&m_obj->file_handle, /* result */
986 0, /* access - ignored due to options value */
987 FALSE, /* inherited by child processes? */
988 DUPLICATE_SAME_ACCESS)) { /* options */
989 dwErr = GetLastError();
990 Py_DECREF(m_obj);
991 PyErr_SetFromWindowsErr(dwErr);
992 return NULL;
994 if (!map_size) {
995 m_obj->size = GetFileSize (fh, NULL);
996 } else {
997 m_obj->size = map_size;
1000 else {
1001 m_obj->size = map_size;
1004 /* set the initial position */
1005 m_obj->pos = (size_t) 0;
1007 /* set the tag name */
1008 if (tagname != NULL && *tagname != '\0') {
1009 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1010 if (m_obj->tagname == NULL) {
1011 PyErr_NoMemory();
1012 Py_DECREF(m_obj);
1013 return NULL;
1015 strcpy(m_obj->tagname, tagname);
1017 else
1018 m_obj->tagname = NULL;
1020 m_obj->access = access;
1021 m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
1022 NULL,
1023 flProtect,
1025 m_obj->size,
1026 m_obj->tagname);
1027 if (m_obj->map_handle != NULL) {
1028 m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
1029 dwDesiredAccess,
1033 if (m_obj->data != NULL) {
1034 return ((PyObject *) m_obj);
1035 } else {
1036 dwErr = GetLastError();
1038 } else {
1039 dwErr = GetLastError();
1041 Py_DECREF(m_obj);
1042 PyErr_SetFromWindowsErr(dwErr);
1043 return (NULL);
1045 #endif /* MS_WIN32 */
1047 /* List of functions exported by this module */
1048 static struct PyMethodDef mmap_functions[] = {
1049 {"mmap", (PyCFunction) new_mmap_object,
1050 METH_VARARGS|METH_KEYWORDS},
1051 {NULL, NULL} /* Sentinel */
1054 DL_EXPORT(void)
1055 initmmap(void)
1057 PyObject *dict, *module;
1059 /* Patch the object type */
1060 mmap_object_type.ob_type = &PyType_Type;
1062 module = Py_InitModule ("mmap", mmap_functions);
1063 dict = PyModule_GetDict (module);
1064 mmap_module_error = PyExc_EnvironmentError;
1065 Py_INCREF(mmap_module_error);
1066 PyDict_SetItemString (dict, "error", mmap_module_error);
1067 #ifdef PROT_EXEC
1068 PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) );
1069 #endif
1070 #ifdef PROT_READ
1071 PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) );
1072 #endif
1073 #ifdef PROT_WRITE
1074 PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) );
1075 #endif
1077 #ifdef MAP_SHARED
1078 PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) );
1079 #endif
1080 #ifdef MAP_PRIVATE
1081 PyDict_SetItemString (dict, "MAP_PRIVATE",
1082 PyInt_FromLong(MAP_PRIVATE) );
1083 #endif
1084 #ifdef MAP_DENYWRITE
1085 PyDict_SetItemString (dict, "MAP_DENYWRITE",
1086 PyInt_FromLong(MAP_DENYWRITE) );
1087 #endif
1088 #ifdef MAP_EXECUTABLE
1089 PyDict_SetItemString (dict, "MAP_EXECUTABLE",
1090 PyInt_FromLong(MAP_EXECUTABLE) );
1091 #endif
1092 #ifdef MAP_ANON
1093 PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANON) );
1094 PyDict_SetItemString (dict, "MAP_ANONYMOUS",
1095 PyInt_FromLong(MAP_ANON) );
1096 #endif
1098 PyDict_SetItemString (dict, "PAGESIZE",
1099 PyInt_FromLong( (long)my_getpagesize() ) );
1101 PyDict_SetItemString (dict, "ACCESS_READ",
1102 PyInt_FromLong(ACCESS_READ));
1103 PyDict_SetItemString (dict, "ACCESS_WRITE",
1104 PyInt_FromLong(ACCESS_WRITE));
1105 PyDict_SetItemString (dict, "ACCESS_COPY",
1106 PyInt_FromLong(ACCESS_COPY));