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
,
169 if (!PyArg_ParseTuple(args
, ":read_byte"))
171 if (self
->pos
< self
->size
) {
172 where
= self
->data
+ self
->pos
;
173 value
= (char) *(where
);
175 return Py_BuildValue("c", (char) *(where
));
177 PyErr_SetString (PyExc_ValueError
, "read byte out of range");
183 mmap_read_line_method(mmap_object
*self
,
186 char *start
= self
->data
+self
->pos
;
187 char *eof
= self
->data
+self
->size
;
192 if (!PyArg_ParseTuple(args
, ":readline"))
195 eol
= memchr(start
, '\n', self
->size
- self
->pos
);
199 ++eol
; /* we're interested in the position after the
201 result
= PyString_FromStringAndSize(start
, (eol
- start
));
202 self
->pos
+= (eol
- start
);
207 mmap_read_method(mmap_object
*self
,
214 if (!PyArg_ParseTuple(args
, "l:read", &num_bytes
))
217 /* silently 'adjust' out-of-range requests */
218 if ((self
->pos
+ num_bytes
) > self
->size
) {
219 num_bytes
-= (self
->pos
+num_bytes
) - self
->size
;
221 result
= Py_BuildValue("s#", self
->data
+self
->pos
, num_bytes
);
222 self
->pos
+= num_bytes
;
227 mmap_find_method(mmap_object
*self
,
230 int start
= self
->pos
;
235 if (!PyArg_ParseTuple (args
, "s#|i:find", &needle
, &len
, &start
)) {
238 char *p
= self
->data
+self
->pos
;
239 char *e
= self
->data
+self
->size
;
243 while ((s
<e
) && (*n
) && !(*s
-*n
)) {
247 return Py_BuildValue (
249 (int) (p
- (self
->data
+ start
)));
253 return Py_BuildValue ("l", (long) -1);
258 mmap_write_method(mmap_object
*self
,
265 if (!PyArg_ParseTuple (args
, "s#:write", &data
, &length
))
268 if ((self
->pos
+ length
) > self
->size
) {
269 PyErr_SetString (PyExc_ValueError
, "data out of range");
272 memcpy (self
->data
+self
->pos
, data
, length
);
273 self
->pos
= self
->pos
+length
;
279 mmap_write_byte_method(mmap_object
*self
,
285 if (!PyArg_ParseTuple (args
, "c:write_byte", &value
))
288 *(self
->data
+self
->pos
) = value
;
295 mmap_size_method(mmap_object
*self
,
299 if (!PyArg_ParseTuple(args
, ":size"))
303 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
304 return (Py_BuildValue (
306 GetFileSize (self
->file_handle
, NULL
)));
308 return (Py_BuildValue ("l", (long) self
->size
) );
310 #endif /* MS_WIN32 */
315 if (-1 == fstat(self
->fd
, &buf
)) {
316 PyErr_SetFromErrno(mmap_module_error
);
319 return (Py_BuildValue ("l", (long) buf
.st_size
) );
324 /* This assumes that you want the entire file mapped,
325 / and when recreating the map will make the new file
328 / Is this really necessary? This could easily be done
329 / from python by just closing and re-opening with the
334 mmap_resize_method(mmap_object
*self
,
337 unsigned long new_size
;
339 if (!PyArg_ParseTuple (args
, "l:resize", &new_size
)) {
344 /* First, unmap the file view */
345 UnmapViewOfFile (self
->data
);
346 /* Close the mapping object */
347 CloseHandle (self
->map_handle
);
348 /* Move to the desired EOF position */
349 SetFilePointer (self
->file_handle
,
350 new_size
, NULL
, FILE_BEGIN
);
351 /* Change the size of the file */
352 SetEndOfFile (self
->file_handle
);
353 /* Create another mapping object and remap the file view */
354 self
->map_handle
= CreateFileMapping (
361 if (self
->map_handle
!= NULL
) {
362 self
->data
= (char *) MapViewOfFile (self
->map_handle
,
367 if (self
->data
!= NULL
) {
368 self
->size
= new_size
;
372 dwErrCode
= GetLastError();
375 dwErrCode
= GetLastError();
377 PyErr_SetFromWindowsErr(dwErrCode
);
379 #endif /* MS_WIN32 */
384 PyErr_SetString(PyExc_SystemError
,
385 "mmap: resizing not available--no mremap()");
391 #ifdef MREMAP_MAYMOVE
392 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
394 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
396 if (newmap
== (void *)-1)
398 PyErr_SetFromErrno(mmap_module_error
);
402 self
->size
= new_size
;
405 #endif /* HAVE_MREMAP */
411 mmap_tell_method(mmap_object
*self
, PyObject
*args
)
414 if (!PyArg_ParseTuple(args
, ":tell"))
416 return (Py_BuildValue ("l", (long) self
->pos
) );
420 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
423 size_t size
= self
->size
;
425 if (!PyArg_ParseTuple (args
, "|ll:flush", &offset
, &size
)) {
427 } else if ((offset
+ size
) > self
->size
) {
428 PyErr_SetString (PyExc_ValueError
,
429 "flush values out of range");
433 return (Py_BuildValue("l", (long)
434 FlushViewOfFile(self
->data
+offset
, size
)));
435 #endif /* MS_WIN32 */
437 /* XXX semantics of return value? */
438 /* XXX flags for msync? */
439 if (-1 == msync(self
->data
+ offset
, size
,
442 PyErr_SetFromErrno(mmap_module_error
);
445 return Py_BuildValue ("l", (long) 0);
451 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
456 if (!PyArg_ParseTuple (args
, "i|i:seek", &dist
, &how
)) {
461 case 0: /* relative to start */
466 case 1: /* relative to current position */
467 if ((int)self
->pos
+ dist
< 0)
469 where
= self
->pos
+ dist
;
471 case 2: /* relative to end */
472 if ((int)self
->size
+ dist
< 0)
474 where
= self
->size
+ dist
;
477 PyErr_SetString (PyExc_ValueError
,
478 "unknown seek type");
481 if (where
> self
->size
)
489 PyErr_SetString (PyExc_ValueError
, "seek out of range");
494 mmap_move_method(mmap_object
*self
, PyObject
*args
)
496 unsigned long dest
, src
, count
;
498 if (!PyArg_ParseTuple (args
, "iii:move", &dest
, &src
, &count
)) {
501 /* bounds check the values */
502 if (/* end of source after end of data?? */
503 ((src
+count
) > self
->size
)
505 || (dest
+count
> self
->size
)) {
506 PyErr_SetString (PyExc_ValueError
,
507 "source or destination out of range");
510 memmove (self
->data
+dest
, self
->data
+src
, count
);
517 static struct PyMethodDef mmap_object_methods
[] = {
518 {"close", (PyCFunction
) mmap_close_method
, 1},
519 {"find", (PyCFunction
) mmap_find_method
, 1},
520 {"flush", (PyCFunction
) mmap_flush_method
, 1},
521 {"move", (PyCFunction
) mmap_move_method
, 1},
522 {"read", (PyCFunction
) mmap_read_method
, 1},
523 {"read_byte", (PyCFunction
) mmap_read_byte_method
, 1},
524 {"readline", (PyCFunction
) mmap_read_line_method
, 1},
525 {"resize", (PyCFunction
) mmap_resize_method
, 1},
526 {"seek", (PyCFunction
) mmap_seek_method
, 1},
527 {"size", (PyCFunction
) mmap_size_method
, 1},
528 {"tell", (PyCFunction
) mmap_tell_method
, 1},
529 {"write", (PyCFunction
) mmap_write_method
, 1},
530 {"write_byte", (PyCFunction
) mmap_write_byte_method
, 1},
531 {NULL
, NULL
} /* sentinel */
534 /* Functions for treating an mmap'ed file as a buffer */
537 mmap_buffer_getreadbuf(mmap_object
*self
, int index
, const void **ptr
)
541 PyErr_SetString(PyExc_SystemError
,
542 "Accessing non-existent mmap segment");
550 mmap_buffer_getwritebuf(mmap_object
*self
, int index
, const void **ptr
)
554 PyErr_SetString(PyExc_SystemError
,
555 "Accessing non-existent mmap segment");
563 mmap_buffer_getsegcount(mmap_object
*self
, int *lenp
)
572 mmap_buffer_getcharbuffer(mmap_object
*self
, int index
, const void **ptr
)
575 PyErr_SetString(PyExc_SystemError
,
576 "accessing non-existent buffer segment");
579 *ptr
= (const char *)self
->data
;
584 mmap_object_getattr(mmap_object
*self
, char *name
)
586 return Py_FindMethod (mmap_object_methods
, (PyObject
*)self
, name
);
590 mmap_length(mmap_object
*self
)
597 mmap_item(mmap_object
*self
, int i
)
600 if (i
< 0 || (size_t)i
>= self
->size
) {
601 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
604 return PyString_FromStringAndSize(self
->data
+ i
, 1);
608 mmap_slice(mmap_object
*self
, int ilow
, int ihigh
)
613 else if ((size_t)ilow
> self
->size
)
619 else if ((size_t)ihigh
> self
->size
)
622 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
626 mmap_concat(mmap_object
*self
, PyObject
*bb
)
629 PyErr_SetString(PyExc_SystemError
,
630 "mmaps don't support concatenation");
635 mmap_repeat(mmap_object
*self
, int n
)
638 PyErr_SetString(PyExc_SystemError
,
639 "mmaps don't support repeat operation");
644 mmap_ass_slice(mmap_object
*self
, int ilow
, int ihigh
, PyObject
*v
)
651 else if ((size_t)ilow
> self
->size
)
657 else if ((size_t)ihigh
> self
->size
)
660 if (! (PyString_Check(v
)) ) {
661 PyErr_SetString(PyExc_IndexError
,
662 "mmap slice assignment must be a string");
665 if ( PyString_Size(v
) != (ihigh
- ilow
) ) {
666 PyErr_SetString(PyExc_IndexError
,
667 "mmap slice assignment is wrong size");
670 buf
= PyString_AsString(v
);
671 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
676 mmap_ass_item(mmap_object
*self
, int i
, PyObject
*v
)
681 if (i
< 0 || (size_t)i
>= self
->size
) {
682 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
685 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
686 PyErr_SetString(PyExc_IndexError
,
687 "mmap assignment must be single-character string");
690 buf
= PyString_AsString(v
);
691 self
->data
[i
] = buf
[0];
695 static PySequenceMethods mmap_as_sequence
= {
696 (inquiry
)mmap_length
, /*sq_length*/
697 (binaryfunc
)mmap_concat
, /*sq_concat*/
698 (intargfunc
)mmap_repeat
, /*sq_repeat*/
699 (intargfunc
)mmap_item
, /*sq_item*/
700 (intintargfunc
)mmap_slice
, /*sq_slice*/
701 (intobjargproc
)mmap_ass_item
, /*sq_ass_item*/
702 (intintobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
705 static PyBufferProcs mmap_as_buffer
= {
706 (getreadbufferproc
)mmap_buffer_getreadbuf
,
707 (getwritebufferproc
)mmap_buffer_getwritebuf
,
708 (getsegcountproc
)mmap_buffer_getsegcount
,
709 (getcharbufferproc
)mmap_buffer_getcharbuffer
,
712 static PyTypeObject mmap_object_type
= {
713 PyObject_HEAD_INIT(0) /* patched in module init */
715 "mmap", /* tp_name */
716 sizeof(mmap_object
), /* tp_size */
719 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
721 (getattrfunc
) mmap_object_getattr
, /* tp_getattr */
725 0, /* tp_as_number */
726 &mmap_as_sequence
, /*tp_as_sequence*/
733 &mmap_as_buffer
, /*tp_as_buffer*/
734 Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
739 /* extract the map size from the given PyObject
741 The map size is restricted to [0, INT_MAX] because this is the current
742 Python limitation on object sizes. Although the mmap object *could* handle
743 a larger map size, there is no point because all the useful operations
744 (len(), slicing(), sequence indexing) are limited by a C int.
746 Returns -1 on error, with an appropriate Python exception raised. On
747 success, the map size is returned. */
749 _GetMapSize(PyObject
*o
)
751 if (PyInt_Check(o
)) {
752 long i
= PyInt_AsLong(o
);
753 if (PyErr_Occurred())
761 else if (PyLong_Check(o
)) {
762 long i
= PyLong_AsLong(o
);
763 if (PyErr_Occurred()) {
764 /* yes negative overflow is mistaken for positive overflow
765 but not worth the trouble to check sign of 'i' */
766 if (PyErr_ExceptionMatches(PyExc_OverflowError
))
778 PyErr_SetString(PyExc_TypeError
,
779 "map size must be an integral value");
784 PyErr_SetString(PyExc_OverflowError
,
785 "memory mapped size must be positive");
789 PyErr_SetString(PyExc_OverflowError
,
790 "memory mapped size is too large (limited by C int)");
796 new_mmap_object(PyObject
*self
, PyObject
*args
, PyObject
*kwdict
)
799 PyObject
*map_size_obj
= NULL
;
801 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
802 char *keywords
[] = {"file", "size", "flags", "prot", NULL
};
804 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
,
806 &fd
, &map_size_obj
, &flags
, &prot
)
809 map_size
= _GetMapSize(map_size_obj
);
813 m_obj
= PyObject_New (mmap_object
, &mmap_object_type
);
814 if (m_obj
== NULL
) {return NULL
;}
815 m_obj
->size
= (size_t) map_size
;
816 m_obj
->pos
= (size_t) 0;
818 m_obj
->data
= mmap(NULL
, map_size
,
821 if (m_obj
->data
== (char *)-1)
824 PyErr_SetFromErrno(mmap_module_error
);
827 return (PyObject
*)m_obj
;
833 new_mmap_object(PyObject
*self
, PyObject
*args
)
836 PyObject
*map_size_obj
= NULL
;
844 if (!PyArg_ParseTuple(args
,
852 map_size
= _GetMapSize(map_size_obj
);
856 /* if an actual filename has been specified */
858 fh
= (HANDLE
)_get_osfhandle(fileno
);
859 if (fh
==(HANDLE
)-1) {
860 PyErr_SetFromErrno(mmap_module_error
);
863 /* Win9x appears to need us seeked to zero */
864 fseek(&_iob
[fileno
], 0, SEEK_SET
);
867 m_obj
= PyObject_New (mmap_object
, &mmap_object_type
);
870 /* Set every field to an invalid marker, so we can safely
871 destruct the object in the face of failure */
873 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
874 m_obj
->map_handle
= INVALID_HANDLE_VALUE
;
875 m_obj
->tagname
= NULL
;
878 /* It is necessary to duplicate the handle, so the
879 Python code can close it on us */
880 if (!DuplicateHandle(
881 GetCurrentProcess(), /* source process handle */
882 fh
, /* handle to be duplicated */
883 GetCurrentProcess(), /* target proc handle */
884 (LPHANDLE
)&m_obj
->file_handle
, /* result */
885 0, /* access - ignored due to options value */
886 FALSE
, /* inherited by child processes? */
887 DUPLICATE_SAME_ACCESS
)) { /* options */
888 dwErr
= GetLastError();
890 PyErr_SetFromWindowsErr(dwErr
);
894 m_obj
->size
= GetFileSize (fh
, NULL
);
896 m_obj
->size
= map_size
;
900 m_obj
->size
= map_size
;
903 /* set the initial position */
904 m_obj
->pos
= (size_t) 0;
906 /* set the tag name */
907 if (tagname
!= NULL
&& *tagname
!= '\0') {
908 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
909 if (m_obj
->tagname
== NULL
) {
914 strcpy(m_obj
->tagname
, tagname
);
917 m_obj
->tagname
= NULL
;
919 m_obj
->map_handle
= CreateFileMapping (m_obj
->file_handle
,
925 if (m_obj
->map_handle
!= NULL
) {
926 m_obj
->data
= (char *) MapViewOfFile (m_obj
->map_handle
,
931 if (m_obj
->data
!= NULL
) {
932 return ((PyObject
*) m_obj
);
934 dwErr
= GetLastError();
937 dwErr
= GetLastError();
940 PyErr_SetFromWindowsErr(dwErr
);
943 #endif /* MS_WIN32 */
945 /* List of functions exported by this module */
946 static struct PyMethodDef mmap_functions
[] = {
947 {"mmap", (PyCFunction
) new_mmap_object
,
948 METH_VARARGS
|METH_KEYWORDS
},
949 {NULL
, NULL
} /* Sentinel */
955 PyObject
*dict
, *module
;
957 /* Patch the object type */
958 mmap_object_type
.ob_type
= &PyType_Type
;
960 module
= Py_InitModule ("mmap", mmap_functions
);
961 dict
= PyModule_GetDict (module
);
962 mmap_module_error
= PyExc_EnvironmentError
;
963 Py_INCREF(mmap_module_error
);
964 PyDict_SetItemString (dict
, "error", mmap_module_error
);
966 PyDict_SetItemString (dict
, "PROT_EXEC", PyInt_FromLong(PROT_EXEC
) );
969 PyDict_SetItemString (dict
, "PROT_READ", PyInt_FromLong(PROT_READ
) );
972 PyDict_SetItemString (dict
, "PROT_WRITE", PyInt_FromLong(PROT_WRITE
) );
976 PyDict_SetItemString (dict
, "MAP_SHARED", PyInt_FromLong(MAP_SHARED
) );
979 PyDict_SetItemString (dict
, "MAP_PRIVATE",
980 PyInt_FromLong(MAP_PRIVATE
) );
983 PyDict_SetItemString (dict
, "MAP_DENYWRITE",
984 PyInt_FromLong(MAP_DENYWRITE
) );
986 #ifdef MAP_EXECUTABLE
987 PyDict_SetItemString (dict
, "MAP_EXECUTABLE",
988 PyInt_FromLong(MAP_EXECUTABLE
) );
991 PyDict_SetItemString (dict
, "MAP_ANON", PyInt_FromLong(MAP_ANON
) );
992 PyDict_SetItemString (dict
, "MAP_ANONYMOUS",
993 PyInt_FromLong(MAP_ANON
) );
996 PyDict_SetItemString (dict
, "PAGESIZE",
997 PyInt_FromLong( (long)my_getpagesize() ) );