2 / Author: Sam Rushing <rushing@nightmare.com>
3 / Hacked for Unix by A.M. Kuchling <amk1@bigfoot.com>
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
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.
44 /* This is missing e.g. on SunOS 4.1.4 */
48 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
52 return sysconf(_SC_PAGESIZE
);
55 #define my_getpagesize getpagesize
61 #include <sys/types.h>
63 static PyObject
*mmap_module_error
;
83 mmap_object_dealloc(mmap_object
*m_obj
)
86 if (m_obj
->data
!= NULL
)
87 UnmapViewOfFile (m_obj
->data
);
88 if (m_obj
->map_handle
!= INVALID_HANDLE_VALUE
)
89 CloseHandle (m_obj
->map_handle
);
90 if (m_obj
->file_handle
!= INVALID_HANDLE_VALUE
)
91 CloseHandle (m_obj
->file_handle
);
93 PyMem_Free(m_obj
->tagname
);
97 if (m_obj
->data
!=NULL
) {
98 msync(m_obj
->data
, m_obj
->size
, MS_SYNC
);
99 munmap(m_obj
->data
, m_obj
->size
);
107 mmap_close_method(mmap_object
*self
, PyObject
*args
)
109 if (!PyArg_ParseTuple(args
, ":close"))
112 /* For each resource we maintain, we need to check
113 the value is valid, and if so, free the resource
114 and set the member value to an invalid value so
115 the dealloc does not attempt to resource clearing
117 TODO - should we check for errors in the close operations???
119 if (self
->data
!= NULL
) {
120 UnmapViewOfFile (self
->data
);
123 if (self
->map_handle
!= INVALID_HANDLE_VALUE
) {
124 CloseHandle (self
->map_handle
);
125 self
->map_handle
= INVALID_HANDLE_VALUE
;
127 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
128 CloseHandle (self
->file_handle
);
129 self
->file_handle
= INVALID_HANDLE_VALUE
;
131 #endif /* MS_WIN32 */
134 munmap(self
->data
, self
->size
);
143 #define CHECK_VALID(err) \
145 if (!self->map_handle) { \
146 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
150 #endif /* MS_WIN32 */
153 #define CHECK_VALID(err) \
155 if (self->data == NULL) { \
156 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
163 mmap_read_byte_method(mmap_object
*self
,
167 if (!PyArg_ParseTuple(args
, ":read_byte"))
169 if (self
->pos
< self
->size
) {
170 char value
= self
->data
[self
->pos
];
172 return Py_BuildValue("c", value
);
174 PyErr_SetString (PyExc_ValueError
, "read byte out of range");
180 mmap_read_line_method(mmap_object
*self
,
183 char *start
= self
->data
+self
->pos
;
184 char *eof
= self
->data
+self
->size
;
189 if (!PyArg_ParseTuple(args
, ":readline"))
192 eol
= memchr(start
, '\n', self
->size
- self
->pos
);
196 ++eol
; /* we're interested in the position after the
198 result
= PyString_FromStringAndSize(start
, (eol
- start
));
199 self
->pos
+= (eol
- start
);
204 mmap_read_method(mmap_object
*self
,
211 if (!PyArg_ParseTuple(args
, "l:read", &num_bytes
))
214 /* silently 'adjust' out-of-range requests */
215 if ((self
->pos
+ num_bytes
) > self
->size
) {
216 num_bytes
-= (self
->pos
+num_bytes
) - self
->size
;
218 result
= Py_BuildValue("s#", self
->data
+self
->pos
, num_bytes
);
219 self
->pos
+= num_bytes
;
224 mmap_find_method(mmap_object
*self
,
227 long start
= self
->pos
;
232 if (!PyArg_ParseTuple (args
, "s#|l:find", &needle
, &len
, &start
)) {
236 char *e
= self
->data
+ self
->size
;
242 else if ((size_t)start
> self
->size
)
244 p
= self
->data
+ start
;
249 while ((s
<e
) && (*n
) && !(*s
-*n
)) {
253 return Py_BuildValue (
255 (long) (p
- self
->data
));
259 return Py_BuildValue ("l", (long) -1);
264 mmap_write_method(mmap_object
*self
,
271 if (!PyArg_ParseTuple (args
, "s#:write", &data
, &length
))
274 if ((self
->pos
+ length
) > self
->size
) {
275 PyErr_SetString (PyExc_ValueError
, "data out of range");
278 memcpy (self
->data
+self
->pos
, data
, length
);
279 self
->pos
= self
->pos
+length
;
285 mmap_write_byte_method(mmap_object
*self
,
291 if (!PyArg_ParseTuple (args
, "c:write_byte", &value
))
294 *(self
->data
+self
->pos
) = value
;
301 mmap_size_method(mmap_object
*self
,
305 if (!PyArg_ParseTuple(args
, ":size"))
309 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
310 return (Py_BuildValue (
312 GetFileSize (self
->file_handle
, NULL
)));
314 return (Py_BuildValue ("l", (long) self
->size
) );
316 #endif /* MS_WIN32 */
321 if (-1 == fstat(self
->fd
, &buf
)) {
322 PyErr_SetFromErrno(mmap_module_error
);
325 return (Py_BuildValue ("l", (long) buf
.st_size
) );
330 /* This assumes that you want the entire file mapped,
331 / and when recreating the map will make the new file
334 / Is this really necessary? This could easily be done
335 / from python by just closing and re-opening with the
340 mmap_resize_method(mmap_object
*self
,
343 unsigned long new_size
;
345 if (!PyArg_ParseTuple (args
, "l:resize", &new_size
)) {
350 /* First, unmap the file view */
351 UnmapViewOfFile (self
->data
);
352 /* Close the mapping object */
353 CloseHandle (self
->map_handle
);
354 /* Move to the desired EOF position */
355 SetFilePointer (self
->file_handle
,
356 new_size
, NULL
, FILE_BEGIN
);
357 /* Change the size of the file */
358 SetEndOfFile (self
->file_handle
);
359 /* Create another mapping object and remap the file view */
360 self
->map_handle
= CreateFileMapping (
367 if (self
->map_handle
!= NULL
) {
368 self
->data
= (char *) MapViewOfFile (self
->map_handle
,
373 if (self
->data
!= NULL
) {
374 self
->size
= new_size
;
378 dwErrCode
= GetLastError();
381 dwErrCode
= GetLastError();
383 PyErr_SetFromWindowsErr(dwErrCode
);
385 #endif /* MS_WIN32 */
390 PyErr_SetString(PyExc_SystemError
,
391 "mmap: resizing not available--no mremap()");
397 #ifdef MREMAP_MAYMOVE
398 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
400 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
402 if (newmap
== (void *)-1)
404 PyErr_SetFromErrno(mmap_module_error
);
408 self
->size
= new_size
;
411 #endif /* HAVE_MREMAP */
417 mmap_tell_method(mmap_object
*self
, PyObject
*args
)
420 if (!PyArg_ParseTuple(args
, ":tell"))
422 return (Py_BuildValue ("l", (long) self
->pos
) );
426 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
429 size_t size
= self
->size
;
431 if (!PyArg_ParseTuple (args
, "|ll:flush", &offset
, &size
)) {
433 } else if ((offset
+ size
) > self
->size
) {
434 PyErr_SetString (PyExc_ValueError
,
435 "flush values out of range");
439 return (Py_BuildValue("l", (long)
440 FlushViewOfFile(self
->data
+offset
, size
)));
441 #endif /* MS_WIN32 */
443 /* XXX semantics of return value? */
444 /* XXX flags for msync? */
445 if (-1 == msync(self
->data
+ offset
, size
,
448 PyErr_SetFromErrno(mmap_module_error
);
451 return Py_BuildValue ("l", (long) 0);
457 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
462 if (!PyArg_ParseTuple (args
, "i|i:seek", &dist
, &how
)) {
467 case 0: /* relative to start */
472 case 1: /* relative to current position */
473 if ((int)self
->pos
+ dist
< 0)
475 where
= self
->pos
+ dist
;
477 case 2: /* relative to end */
478 if ((int)self
->size
+ dist
< 0)
480 where
= self
->size
+ dist
;
483 PyErr_SetString (PyExc_ValueError
,
484 "unknown seek type");
487 if (where
> self
->size
)
495 PyErr_SetString (PyExc_ValueError
, "seek out of range");
500 mmap_move_method(mmap_object
*self
, PyObject
*args
)
502 unsigned long dest
, src
, count
;
504 if (!PyArg_ParseTuple (args
, "iii:move", &dest
, &src
, &count
)) {
507 /* bounds check the values */
508 if (/* end of source after end of data?? */
509 ((src
+count
) > self
->size
)
511 || (dest
+count
> self
->size
)) {
512 PyErr_SetString (PyExc_ValueError
,
513 "source or destination out of range");
516 memmove (self
->data
+dest
, self
->data
+src
, count
);
523 static struct PyMethodDef mmap_object_methods
[] = {
524 {"close", (PyCFunction
) mmap_close_method
, 1},
525 {"find", (PyCFunction
) mmap_find_method
, 1},
526 {"flush", (PyCFunction
) mmap_flush_method
, 1},
527 {"move", (PyCFunction
) mmap_move_method
, 1},
528 {"read", (PyCFunction
) mmap_read_method
, 1},
529 {"read_byte", (PyCFunction
) mmap_read_byte_method
, 1},
530 {"readline", (PyCFunction
) mmap_read_line_method
, 1},
531 {"resize", (PyCFunction
) mmap_resize_method
, 1},
532 {"seek", (PyCFunction
) mmap_seek_method
, 1},
533 {"size", (PyCFunction
) mmap_size_method
, 1},
534 {"tell", (PyCFunction
) mmap_tell_method
, 1},
535 {"write", (PyCFunction
) mmap_write_method
, 1},
536 {"write_byte", (PyCFunction
) mmap_write_byte_method
, 1},
537 {NULL
, NULL
} /* sentinel */
540 /* Functions for treating an mmap'ed file as a buffer */
543 mmap_buffer_getreadbuf(mmap_object
*self
, int index
, const void **ptr
)
547 PyErr_SetString(PyExc_SystemError
,
548 "Accessing non-existent mmap segment");
556 mmap_buffer_getwritebuf(mmap_object
*self
, int index
, const void **ptr
)
560 PyErr_SetString(PyExc_SystemError
,
561 "Accessing non-existent mmap segment");
569 mmap_buffer_getsegcount(mmap_object
*self
, int *lenp
)
578 mmap_buffer_getcharbuffer(mmap_object
*self
, int index
, const void **ptr
)
581 PyErr_SetString(PyExc_SystemError
,
582 "accessing non-existent buffer segment");
585 *ptr
= (const char *)self
->data
;
590 mmap_object_getattr(mmap_object
*self
, char *name
)
592 return Py_FindMethod (mmap_object_methods
, (PyObject
*)self
, name
);
596 mmap_length(mmap_object
*self
)
603 mmap_item(mmap_object
*self
, int i
)
606 if (i
< 0 || (size_t)i
>= self
->size
) {
607 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
610 return PyString_FromStringAndSize(self
->data
+ i
, 1);
614 mmap_slice(mmap_object
*self
, int ilow
, int ihigh
)
619 else if ((size_t)ilow
> self
->size
)
625 else if ((size_t)ihigh
> self
->size
)
628 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
632 mmap_concat(mmap_object
*self
, PyObject
*bb
)
635 PyErr_SetString(PyExc_SystemError
,
636 "mmaps don't support concatenation");
641 mmap_repeat(mmap_object
*self
, int n
)
644 PyErr_SetString(PyExc_SystemError
,
645 "mmaps don't support repeat operation");
650 mmap_ass_slice(mmap_object
*self
, int ilow
, int ihigh
, PyObject
*v
)
657 else if ((size_t)ilow
> self
->size
)
663 else if ((size_t)ihigh
> self
->size
)
667 PyErr_SetString(PyExc_TypeError
,
668 "mmap object doesn't support slice deletion");
671 if (! (PyString_Check(v
)) ) {
672 PyErr_SetString(PyExc_IndexError
,
673 "mmap slice assignment must be a string");
676 if ( PyString_Size(v
) != (ihigh
- ilow
) ) {
677 PyErr_SetString(PyExc_IndexError
,
678 "mmap slice assignment is wrong size");
681 buf
= PyString_AsString(v
);
682 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
687 mmap_ass_item(mmap_object
*self
, int i
, PyObject
*v
)
692 if (i
< 0 || (size_t)i
>= self
->size
) {
693 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
697 PyErr_SetString(PyExc_TypeError
,
698 "mmap object doesn't support item deletion");
701 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
702 PyErr_SetString(PyExc_IndexError
,
703 "mmap assignment must be single-character string");
706 buf
= PyString_AsString(v
);
707 self
->data
[i
] = buf
[0];
711 static PySequenceMethods mmap_as_sequence
= {
712 (inquiry
)mmap_length
, /*sq_length*/
713 (binaryfunc
)mmap_concat
, /*sq_concat*/
714 (intargfunc
)mmap_repeat
, /*sq_repeat*/
715 (intargfunc
)mmap_item
, /*sq_item*/
716 (intintargfunc
)mmap_slice
, /*sq_slice*/
717 (intobjargproc
)mmap_ass_item
, /*sq_ass_item*/
718 (intintobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
721 static PyBufferProcs mmap_as_buffer
= {
722 (getreadbufferproc
)mmap_buffer_getreadbuf
,
723 (getwritebufferproc
)mmap_buffer_getwritebuf
,
724 (getsegcountproc
)mmap_buffer_getsegcount
,
725 (getcharbufferproc
)mmap_buffer_getcharbuffer
,
728 static PyTypeObject mmap_object_type
= {
729 PyObject_HEAD_INIT(0) /* patched in module init */
731 "mmap", /* tp_name */
732 sizeof(mmap_object
), /* tp_size */
735 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
737 (getattrfunc
) mmap_object_getattr
, /* tp_getattr */
741 0, /* tp_as_number */
742 &mmap_as_sequence
, /*tp_as_sequence*/
749 &mmap_as_buffer
, /*tp_as_buffer*/
750 Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
755 /* extract the map size from the given PyObject
757 The map size is restricted to [0, INT_MAX] because this is the current
758 Python limitation on object sizes. Although the mmap object *could* handle
759 a larger map size, there is no point because all the useful operations
760 (len(), slicing(), sequence indexing) are limited by a C int.
762 Returns -1 on error, with an appropriate Python exception raised. On
763 success, the map size is returned. */
765 _GetMapSize(PyObject
*o
)
767 if (PyInt_Check(o
)) {
768 long i
= PyInt_AsLong(o
);
769 if (PyErr_Occurred())
777 else if (PyLong_Check(o
)) {
778 long i
= PyLong_AsLong(o
);
779 if (PyErr_Occurred()) {
780 /* yes negative overflow is mistaken for positive overflow
781 but not worth the trouble to check sign of 'i' */
782 if (PyErr_ExceptionMatches(PyExc_OverflowError
))
794 PyErr_SetString(PyExc_TypeError
,
795 "map size must be an integral value");
800 PyErr_SetString(PyExc_OverflowError
,
801 "memory mapped size must be positive");
805 PyErr_SetString(PyExc_OverflowError
,
806 "memory mapped size is too large (limited by C int)");
812 new_mmap_object(PyObject
*self
, PyObject
*args
, PyObject
*kwdict
)
815 PyObject
*map_size_obj
= NULL
;
817 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
818 char *keywords
[] = {"file", "size", "flags", "prot", NULL
};
820 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
,
822 &fd
, &map_size_obj
, &flags
, &prot
)
825 map_size
= _GetMapSize(map_size_obj
);
829 m_obj
= PyObject_New (mmap_object
, &mmap_object_type
);
830 if (m_obj
== NULL
) {return NULL
;}
831 m_obj
->size
= (size_t) map_size
;
832 m_obj
->pos
= (size_t) 0;
834 m_obj
->data
= mmap(NULL
, map_size
,
837 if (m_obj
->data
== (char *)-1)
840 PyErr_SetFromErrno(mmap_module_error
);
843 return (PyObject
*)m_obj
;
849 new_mmap_object(PyObject
*self
, PyObject
*args
)
852 PyObject
*map_size_obj
= NULL
;
860 if (!PyArg_ParseTuple(args
,
868 map_size
= _GetMapSize(map_size_obj
);
872 /* if an actual filename has been specified */
874 fh
= (HANDLE
)_get_osfhandle(fileno
);
875 if (fh
==(HANDLE
)-1) {
876 PyErr_SetFromErrno(mmap_module_error
);
879 /* Win9x appears to need us seeked to zero */
880 fseek(&_iob
[fileno
], 0, SEEK_SET
);
883 m_obj
= PyObject_New (mmap_object
, &mmap_object_type
);
886 /* Set every field to an invalid marker, so we can safely
887 destruct the object in the face of failure */
889 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
890 m_obj
->map_handle
= INVALID_HANDLE_VALUE
;
891 m_obj
->tagname
= NULL
;
894 /* It is necessary to duplicate the handle, so the
895 Python code can close it on us */
896 if (!DuplicateHandle(
897 GetCurrentProcess(), /* source process handle */
898 fh
, /* handle to be duplicated */
899 GetCurrentProcess(), /* target proc handle */
900 (LPHANDLE
)&m_obj
->file_handle
, /* result */
901 0, /* access - ignored due to options value */
902 FALSE
, /* inherited by child processes? */
903 DUPLICATE_SAME_ACCESS
)) { /* options */
904 dwErr
= GetLastError();
906 PyErr_SetFromWindowsErr(dwErr
);
910 m_obj
->size
= GetFileSize (fh
, NULL
);
912 m_obj
->size
= map_size
;
916 m_obj
->size
= map_size
;
919 /* set the initial position */
920 m_obj
->pos
= (size_t) 0;
922 /* set the tag name */
923 if (tagname
!= NULL
&& *tagname
!= '\0') {
924 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
925 if (m_obj
->tagname
== NULL
) {
930 strcpy(m_obj
->tagname
, tagname
);
933 m_obj
->tagname
= NULL
;
935 m_obj
->map_handle
= CreateFileMapping (m_obj
->file_handle
,
941 if (m_obj
->map_handle
!= NULL
) {
942 m_obj
->data
= (char *) MapViewOfFile (m_obj
->map_handle
,
947 if (m_obj
->data
!= NULL
) {
948 return ((PyObject
*) m_obj
);
950 dwErr
= GetLastError();
953 dwErr
= GetLastError();
956 PyErr_SetFromWindowsErr(dwErr
);
959 #endif /* MS_WIN32 */
961 /* List of functions exported by this module */
962 static struct PyMethodDef mmap_functions
[] = {
963 {"mmap", (PyCFunction
) new_mmap_object
,
964 METH_VARARGS
|METH_KEYWORDS
},
965 {NULL
, NULL
} /* Sentinel */
971 PyObject
*dict
, *module
;
973 /* Patch the object type */
974 mmap_object_type
.ob_type
= &PyType_Type
;
976 module
= Py_InitModule ("mmap", mmap_functions
);
977 dict
= PyModule_GetDict (module
);
978 mmap_module_error
= PyExc_EnvironmentError
;
979 Py_INCREF(mmap_module_error
);
980 PyDict_SetItemString (dict
, "error", mmap_module_error
);
982 PyDict_SetItemString (dict
, "PROT_EXEC", PyInt_FromLong(PROT_EXEC
) );
985 PyDict_SetItemString (dict
, "PROT_READ", PyInt_FromLong(PROT_READ
) );
988 PyDict_SetItemString (dict
, "PROT_WRITE", PyInt_FromLong(PROT_WRITE
) );
992 PyDict_SetItemString (dict
, "MAP_SHARED", PyInt_FromLong(MAP_SHARED
) );
995 PyDict_SetItemString (dict
, "MAP_PRIVATE",
996 PyInt_FromLong(MAP_PRIVATE
) );
999 PyDict_SetItemString (dict
, "MAP_DENYWRITE",
1000 PyInt_FromLong(MAP_DENYWRITE
) );
1002 #ifdef MAP_EXECUTABLE
1003 PyDict_SetItemString (dict
, "MAP_EXECUTABLE",
1004 PyInt_FromLong(MAP_EXECUTABLE
) );
1007 PyDict_SetItemString (dict
, "MAP_ANON", PyInt_FromLong(MAP_ANON
) );
1008 PyDict_SetItemString (dict
, "MAP_ANONYMOUS",
1009 PyInt_FromLong(MAP_ANON
) );
1012 PyDict_SetItemString (dict
, "PAGESIZE",
1013 PyInt_FromLong( (long)my_getpagesize() ) );