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.
38 #include <sys/types.h>
40 static PyObject
*mmap_module_error
;
60 mmap_object_dealloc(mmap_object
*m_obj
)
63 if (m_obj
->data
!= NULL
)
64 UnmapViewOfFile (m_obj
->data
);
65 if (m_obj
->map_handle
!= INVALID_HANDLE_VALUE
)
66 CloseHandle (m_obj
->map_handle
);
67 if (m_obj
->file_handle
!= INVALID_HANDLE_VALUE
)
68 CloseHandle (m_obj
->file_handle
);
70 PyMem_Free(m_obj
->tagname
);
74 if (m_obj
->data
!=NULL
) {
75 msync(m_obj
->data
, m_obj
->size
, MS_SYNC
);
76 munmap(m_obj
->data
, m_obj
->size
);
84 mmap_close_method(mmap_object
*self
, PyObject
*args
)
86 if (!PyArg_ParseTuple(args
, ":close"))
89 /* For each resource we maintain, we need to check
90 the value is valid, and if so, free the resource
91 and set the member value to an invalid value so
92 the dealloc does not attempt to resource clearing
94 TODO - should we check for errors in the close operations???
96 if (self
->data
!= NULL
) {
97 UnmapViewOfFile (self
->data
);
100 if (self
->map_handle
!= INVALID_HANDLE_VALUE
) {
101 CloseHandle (self
->map_handle
);
102 self
->map_handle
= INVALID_HANDLE_VALUE
;
104 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
105 CloseHandle (self
->file_handle
);
106 self
->file_handle
= INVALID_HANDLE_VALUE
;
108 #endif /* MS_WIN32 */
111 munmap(self
->data
, self
->size
);
120 #define CHECK_VALID(err) \
122 if (!self->map_handle) { \
123 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
127 #endif /* MS_WIN32 */
130 #define CHECK_VALID(err) \
132 if (self->data == NULL) { \
133 PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \
140 mmap_read_byte_method(mmap_object
*self
,
146 if (!PyArg_ParseTuple(args
, ":read_byte"))
148 if (self
->pos
< self
->size
) {
149 where
= self
->data
+ self
->pos
;
150 value
= (char) *(where
);
152 return Py_BuildValue("c", (char) *(where
));
154 PyErr_SetString (PyExc_ValueError
, "read byte out of range");
160 mmap_read_line_method(mmap_object
*self
,
163 char *start
= self
->data
+self
->pos
;
164 char *eof
= self
->data
+self
->size
;
169 if (!PyArg_ParseTuple(args
, ":readline"))
172 eol
= memchr(start
, '\n', self
->size
- self
->pos
);
176 ++eol
; /* we're interested in the position after the
178 result
= PyString_FromStringAndSize(start
, (eol
- start
));
179 self
->pos
+= (eol
- start
);
184 mmap_read_method(mmap_object
*self
,
191 if (!PyArg_ParseTuple(args
, "l:read", &num_bytes
))
194 /* silently 'adjust' out-of-range requests */
195 if ((self
->pos
+ num_bytes
) > self
->size
) {
196 num_bytes
-= (self
->pos
+num_bytes
) - self
->size
;
198 result
= Py_BuildValue("s#", self
->data
+self
->pos
, num_bytes
);
199 self
->pos
+= num_bytes
;
204 mmap_find_method(mmap_object
*self
,
207 int start
= self
->pos
;
212 if (!PyArg_ParseTuple (args
, "s#|i:find", &needle
, &len
, &start
)) {
215 char *p
= self
->data
+self
->pos
;
216 char *e
= self
->data
+self
->size
;
220 while ((s
<e
) && (*n
) && !(*s
-*n
)) {
224 return Py_BuildValue (
226 (int) (p
- (self
->data
+ start
)));
230 return Py_BuildValue ("l", (long) -1);
235 mmap_write_method(mmap_object
*self
,
242 if (!PyArg_ParseTuple (args
, "s#:write", &data
, &length
))
245 if ((self
->pos
+ length
) > self
->size
) {
246 PyErr_SetString (PyExc_ValueError
, "data out of range");
249 memcpy (self
->data
+self
->pos
, data
, length
);
250 self
->pos
= self
->pos
+length
;
256 mmap_write_byte_method(mmap_object
*self
,
262 if (!PyArg_ParseTuple (args
, "c:write_byte", &value
))
265 *(self
->data
+self
->pos
) = value
;
272 mmap_size_method(mmap_object
*self
,
276 if (!PyArg_ParseTuple(args
, ":size"))
280 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
281 return (Py_BuildValue (
283 GetFileSize (self
->file_handle
, NULL
)));
285 return (Py_BuildValue ("l", (long) self
->size
) );
287 #endif /* MS_WIN32 */
292 if (-1 == fstat(self
->fd
, &buf
)) {
293 PyErr_SetFromErrno(mmap_module_error
);
296 return (Py_BuildValue ("l", (long) buf
.st_size
) );
301 /* This assumes that you want the entire file mapped,
302 / and when recreating the map will make the new file
305 / Is this really necessary? This could easily be done
306 / from python by just closing and re-opening with the
311 mmap_resize_method(mmap_object
*self
,
314 unsigned long new_size
;
316 if (!PyArg_ParseTuple (args
, "l:resize", &new_size
)) {
321 /* First, unmap the file view */
322 UnmapViewOfFile (self
->data
);
323 /* Close the mapping object */
324 CloseHandle (self
->map_handle
);
325 /* Move to the desired EOF position */
326 SetFilePointer (self
->file_handle
,
327 new_size
, NULL
, FILE_BEGIN
);
328 /* Change the size of the file */
329 SetEndOfFile (self
->file_handle
);
330 /* Create another mapping object and remap the file view */
331 self
->map_handle
= CreateFileMapping (
338 if (self
->map_handle
!= NULL
) {
339 self
->data
= (char *) MapViewOfFile (self
->map_handle
,
344 if (self
->data
!= NULL
) {
345 self
->size
= new_size
;
349 dwErrCode
= GetLastError();
352 dwErrCode
= GetLastError();
354 PyErr_SetFromWindowsErr(dwErrCode
);
356 #endif /* MS_WIN32 */
361 PyErr_SetString(PyExc_SystemError
,
362 "mmap: resizing not available--no mremap()");
368 #ifdef MREMAP_MAYMOVE
369 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
371 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
373 if (newmap
== (void *)-1)
375 PyErr_SetFromErrno(mmap_module_error
);
379 self
->size
= new_size
;
382 #endif /* HAVE_MREMAP */
388 mmap_tell_method(mmap_object
*self
, PyObject
*args
)
391 if (!PyArg_ParseTuple(args
, ":tell"))
393 return (Py_BuildValue ("l", (long) self
->pos
) );
397 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
400 size_t size
= self
->size
;
402 if (!PyArg_ParseTuple (args
, "|ll:flush", &offset
, &size
)) {
404 } else if ((offset
+ size
) > self
->size
) {
405 PyErr_SetString (PyExc_ValueError
,
406 "flush values out of range");
410 return (Py_BuildValue("l", (long)
411 FlushViewOfFile(self
->data
+offset
, size
)));
412 #endif /* MS_WIN32 */
414 /* XXX semantics of return value? */
415 /* XXX flags for msync? */
416 if (-1 == msync(self
->data
+ offset
, size
,
419 PyErr_SetFromErrno(mmap_module_error
);
422 return Py_BuildValue ("l", (long) 0);
428 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
433 if (!PyArg_ParseTuple (args
, "i|i:seek", &dist
, &how
)) {
438 case 0: /* relative to start */
443 case 1: /* relative to current position */
444 if ((int)self
->pos
+ dist
< 0)
446 where
= self
->pos
+ dist
;
448 case 2: /* relative to end */
449 if ((int)self
->size
+ dist
< 0)
451 where
= self
->size
+ dist
;
454 PyErr_SetString (PyExc_ValueError
,
455 "unknown seek type");
458 if (where
> self
->size
)
466 PyErr_SetString (PyExc_ValueError
, "seek out of range");
471 mmap_move_method(mmap_object
*self
, PyObject
*args
)
473 unsigned long dest
, src
, count
;
475 if (!PyArg_ParseTuple (args
, "iii:move", &dest
, &src
, &count
)) {
478 /* bounds check the values */
479 if (/* end of source after end of data?? */
480 ((src
+count
) > self
->size
)
482 || (dest
+count
> self
->size
)) {
483 PyErr_SetString (PyExc_ValueError
,
484 "source or destination out of range");
487 memmove (self
->data
+dest
, self
->data
+src
, count
);
494 static struct PyMethodDef mmap_object_methods
[] = {
495 {"close", (PyCFunction
) mmap_close_method
, 1},
496 {"find", (PyCFunction
) mmap_find_method
, 1},
497 {"flush", (PyCFunction
) mmap_flush_method
, 1},
498 {"move", (PyCFunction
) mmap_move_method
, 1},
499 {"read", (PyCFunction
) mmap_read_method
, 1},
500 {"read_byte", (PyCFunction
) mmap_read_byte_method
, 1},
501 {"readline", (PyCFunction
) mmap_read_line_method
, 1},
502 {"resize", (PyCFunction
) mmap_resize_method
, 1},
503 {"seek", (PyCFunction
) mmap_seek_method
, 1},
504 {"size", (PyCFunction
) mmap_size_method
, 1},
505 {"tell", (PyCFunction
) mmap_tell_method
, 1},
506 {"write", (PyCFunction
) mmap_write_method
, 1},
507 {"write_byte", (PyCFunction
) mmap_write_byte_method
, 1},
508 {NULL
, NULL
} /* sentinel */
511 /* Functions for treating an mmap'ed file as a buffer */
514 mmap_buffer_getreadbuf(mmap_object
*self
, int index
, const void **ptr
)
518 PyErr_SetString(PyExc_SystemError
,
519 "Accessing non-existent mmap segment");
527 mmap_buffer_getwritebuf(mmap_object
*self
, int index
, const void **ptr
)
531 PyErr_SetString(PyExc_SystemError
,
532 "Accessing non-existent mmap segment");
540 mmap_buffer_getsegcount(mmap_object
*self
, int *lenp
)
549 mmap_buffer_getcharbuffer(mmap_object
*self
, int index
, const void **ptr
)
552 PyErr_SetString(PyExc_SystemError
,
553 "accessing non-existent buffer segment");
556 *ptr
= (const char *)self
->data
;
561 mmap_object_getattr(mmap_object
*self
, char *name
)
563 return Py_FindMethod (mmap_object_methods
, (PyObject
*)self
, name
);
567 mmap_length(mmap_object
*self
)
574 mmap_item(mmap_object
*self
, int i
)
577 if (i
< 0 || (size_t)i
>= self
->size
) {
578 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
581 return PyString_FromStringAndSize(self
->data
+ i
, 1);
585 mmap_slice(mmap_object
*self
, int ilow
, int ihigh
)
590 else if ((size_t)ilow
> self
->size
)
596 else if ((size_t)ihigh
> self
->size
)
599 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
603 mmap_concat(mmap_object
*self
, PyObject
*bb
)
606 PyErr_SetString(PyExc_SystemError
,
607 "mmaps don't support concatenation");
612 mmap_repeat(mmap_object
*self
, int n
)
615 PyErr_SetString(PyExc_SystemError
,
616 "mmaps don't support repeat operation");
621 mmap_ass_slice(mmap_object
*self
, int ilow
, int ihigh
, PyObject
*v
)
628 else if ((size_t)ilow
> self
->size
)
634 else if ((size_t)ihigh
> self
->size
)
637 if (! (PyString_Check(v
)) ) {
638 PyErr_SetString(PyExc_IndexError
,
639 "mmap slice assignment must be a string");
642 if ( PyString_Size(v
) != (ihigh
- ilow
) ) {
643 PyErr_SetString(PyExc_IndexError
,
644 "mmap slice assignment is wrong size");
647 buf
= PyString_AsString(v
);
648 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
653 mmap_ass_item(mmap_object
*self
, int i
, PyObject
*v
)
658 if (i
< 0 || (size_t)i
>= self
->size
) {
659 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
662 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
663 PyErr_SetString(PyExc_IndexError
,
664 "mmap assignment must be single-character string");
667 buf
= PyString_AsString(v
);
668 self
->data
[i
] = buf
[0];
672 static PySequenceMethods mmap_as_sequence
= {
673 (inquiry
)mmap_length
, /*sq_length*/
674 (binaryfunc
)mmap_concat
, /*sq_concat*/
675 (intargfunc
)mmap_repeat
, /*sq_repeat*/
676 (intargfunc
)mmap_item
, /*sq_item*/
677 (intintargfunc
)mmap_slice
, /*sq_slice*/
678 (intobjargproc
)mmap_ass_item
, /*sq_ass_item*/
679 (intintobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
682 static PyBufferProcs mmap_as_buffer
= {
683 (getreadbufferproc
)mmap_buffer_getreadbuf
,
684 (getwritebufferproc
)mmap_buffer_getwritebuf
,
685 (getsegcountproc
)mmap_buffer_getsegcount
,
686 (getcharbufferproc
)mmap_buffer_getcharbuffer
,
689 static PyTypeObject mmap_object_type
= {
690 PyObject_HEAD_INIT(0) /* patched in module init */
692 "mmap", /* tp_name */
693 sizeof(mmap_object
), /* tp_size */
696 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
698 (getattrfunc
) mmap_object_getattr
, /* tp_getattr */
702 0, /* tp_as_number */
703 &mmap_as_sequence
, /*tp_as_sequence*/
710 &mmap_as_buffer
, /*tp_as_buffer*/
711 Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
716 /* extract the map size from the given PyObject
718 The map size is restricted to [0, INT_MAX] because this is the current
719 Python limitation on object sizes. Although the mmap object *could* handle
720 a larger map size, there is no point because all the useful operations
721 (len(), slicing(), sequence indexing) are limited by a C int.
723 Returns -1 on error, with an appropriate Python exception raised. On
724 success, the map size is returned. */
726 _GetMapSize(PyObject
*o
)
728 if (PyInt_Check(o
)) {
729 long i
= PyInt_AsLong(o
);
730 if (PyErr_Occurred())
738 else if (PyLong_Check(o
)) {
739 long i
= PyLong_AsLong(o
);
740 if (PyErr_Occurred()) {
741 /* yes negative overflow is mistaken for positive overflow
742 but not worth the trouble to check sign of 'i' */
743 if (PyErr_ExceptionMatches(PyExc_OverflowError
))
755 PyErr_SetString(PyExc_TypeError
,
756 "map size must be an integral value");
761 PyErr_SetString(PyExc_OverflowError
,
762 "memory mapped size must be positive");
766 PyErr_SetString(PyExc_OverflowError
,
767 "memory mapped size is too large (limited by C int)");
773 new_mmap_object(PyObject
*self
, PyObject
*args
, PyObject
*kwdict
)
776 PyObject
*map_size_obj
= NULL
;
778 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
779 char *keywords
[] = {"file", "size", "flags", "prot", NULL
};
781 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
,
783 &fd
, &map_size_obj
, &flags
, &prot
)
786 map_size
= _GetMapSize(map_size_obj
);
790 m_obj
= PyObject_New (mmap_object
, &mmap_object_type
);
791 if (m_obj
== NULL
) {return NULL
;}
792 m_obj
->size
= (size_t) map_size
;
793 m_obj
->pos
= (size_t) 0;
795 m_obj
->data
= mmap(NULL
, map_size
,
798 if (m_obj
->data
== (void *)-1)
801 PyErr_SetFromErrno(mmap_module_error
);
804 return (PyObject
*)m_obj
;
810 new_mmap_object(PyObject
*self
, PyObject
*args
)
813 PyObject
*map_size_obj
= NULL
;
821 /* Patch the object type */
822 mmap_object_type
.ob_type
= &PyType_Type
;
824 if (!PyArg_ParseTuple(args
,
832 map_size
= _GetMapSize(map_size_obj
);
836 /* if an actual filename has been specified */
838 fh
= (HANDLE
)_get_osfhandle(fileno
);
839 if (fh
==(HANDLE
)-1) {
840 PyErr_SetFromErrno(mmap_module_error
);
843 /* Win9x appears to need us seeked to zero */
844 fseek(&_iob
[fileno
], 0, SEEK_SET
);
847 m_obj
= PyObject_New (mmap_object
, &mmap_object_type
);
850 /* Set every field to an invalid marker, so we can safely
851 destruct the object in the face of failure */
853 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
854 m_obj
->map_handle
= INVALID_HANDLE_VALUE
;
855 m_obj
->tagname
= NULL
;
858 /* It is necessary to duplicate the handle, so the
859 Python code can close it on us */
860 if (!DuplicateHandle(
861 GetCurrentProcess(), /* source process handle */
862 fh
, /* handle to be duplicated */
863 GetCurrentProcess(), /* target proc handle */
864 (LPHANDLE
)&m_obj
->file_handle
, /* result */
865 0, /* access - ignored due to options value */
866 FALSE
, /* inherited by child processes? */
867 DUPLICATE_SAME_ACCESS
)) { /* options */
868 dwErr
= GetLastError();
870 PyErr_SetFromWindowsErr(dwErr
);
874 m_obj
->size
= GetFileSize (fh
, NULL
);
876 m_obj
->size
= map_size
;
880 m_obj
->size
= map_size
;
883 /* set the initial position */
884 m_obj
->pos
= (size_t) 0;
886 /* set the tag name */
887 if (tagname
!= NULL
) {
888 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
889 if (m_obj
->tagname
== NULL
) {
894 strcpy(m_obj
->tagname
, tagname
);
897 m_obj
->tagname
= NULL
;
899 m_obj
->map_handle
= CreateFileMapping (m_obj
->file_handle
,
905 if (m_obj
->map_handle
!= NULL
) {
906 m_obj
->data
= (char *) MapViewOfFile (m_obj
->map_handle
,
911 if (m_obj
->data
!= NULL
) {
912 return ((PyObject
*) m_obj
);
914 dwErr
= GetLastError();
917 dwErr
= GetLastError();
920 PyErr_SetFromWindowsErr(dwErr
);
923 #endif /* MS_WIN32 */
925 /* List of functions exported by this module */
926 static struct PyMethodDef mmap_functions
[] = {
927 {"mmap", (PyCFunction
) new_mmap_object
,
928 METH_VARARGS
|METH_KEYWORDS
},
929 {NULL
, NULL
} /* Sentinel */
933 __declspec(dllexport
) void
934 #endif /* MS_WIN32 */
941 PyObject
*dict
, *module
;
942 module
= Py_InitModule ("mmap", mmap_functions
);
943 dict
= PyModule_GetDict (module
);
944 mmap_module_error
= PyExc_EnvironmentError
;
945 Py_INCREF(mmap_module_error
);
946 PyDict_SetItemString (dict
, "error", mmap_module_error
);
948 PyDict_SetItemString (dict
, "PROT_EXEC", PyInt_FromLong(PROT_EXEC
) );
951 PyDict_SetItemString (dict
, "PROT_READ", PyInt_FromLong(PROT_READ
) );
954 PyDict_SetItemString (dict
, "PROT_WRITE", PyInt_FromLong(PROT_WRITE
) );
958 PyDict_SetItemString (dict
, "MAP_SHARED", PyInt_FromLong(MAP_SHARED
) );
961 PyDict_SetItemString (dict
, "MAP_PRIVATE",
962 PyInt_FromLong(MAP_PRIVATE
) );
965 PyDict_SetItemString (dict
, "MAP_DENYWRITE",
966 PyInt_FromLong(MAP_DENYWRITE
) );
968 #ifdef MAP_EXECUTABLE
969 PyDict_SetItemString (dict
, "MAP_EXECUTABLE",
970 PyInt_FromLong(MAP_EXECUTABLE
) );
973 PyDict_SetItemString (dict
, "MAP_ANON", PyInt_FromLong(MAP_ANON
) );
974 PyDict_SetItemString (dict
, "MAP_ANONYMOUS",
975 PyInt_FromLong(MAP_ANON
) );
979 PyDict_SetItemString (dict
, "PAGESIZE",
980 PyInt_FromLong( (long)getpagesize() ) );
986 PyDict_SetItemString (dict
, "PAGESIZE",
987 PyInt_FromLong( si
.dwPageSize
) );
989 #endif /* MS_WIN32 */