2 / Author: Sam Rushing <rushing@nightmare.com>
3 / Hacked for Unix by AMK
6 / Modified to support mmap with offset - to map a 'window' of a file
7 / Author: Yotam Medini yotamm@mellanox.co.il
9 / mmapmodule.cpp -- map a view of a file into memory
11 / todo: need permission flags, perhaps a 'chsize' analog
12 / not all functions check range yet!!!
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 #define PY_SSIZE_T_CLEAN
39 my_getallocationgranularity (void)
44 return si
.dwAllocationGranularity
;
53 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
57 return sysconf(_SC_PAGESIZE
);
60 #define my_getallocationgranularity my_getpagesize
62 #define my_getpagesize getpagesize
69 #ifdef HAVE_SYS_TYPES_H
70 #include <sys/types.h>
71 #endif /* HAVE_SYS_TYPES_H */
73 /* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
74 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
75 # define MAP_ANONYMOUS MAP_ANON
78 static PyObject
*mmap_module_error
;
92 size_t pos
; /* relative to offset */
110 mmap_object_dealloc(mmap_object
*m_obj
)
113 if (m_obj
->data
!= NULL
)
114 UnmapViewOfFile (m_obj
->data
);
115 if (m_obj
->map_handle
!= NULL
)
116 CloseHandle (m_obj
->map_handle
);
117 if (m_obj
->file_handle
!= INVALID_HANDLE_VALUE
)
118 CloseHandle (m_obj
->file_handle
);
120 PyMem_Free(m_obj
->tagname
);
121 #endif /* MS_WINDOWS */
125 (void) close(m_obj
->fd
);
126 if (m_obj
->data
!=NULL
) {
127 msync(m_obj
->data
, m_obj
->size
, MS_SYNC
);
128 munmap(m_obj
->data
, m_obj
->size
);
132 Py_TYPE(m_obj
)->tp_free((PyObject
*)m_obj
);
136 mmap_close_method(mmap_object
*self
, PyObject
*unused
)
139 /* For each resource we maintain, we need to check
140 the value is valid, and if so, free the resource
141 and set the member value to an invalid value so
142 the dealloc does not attempt to resource clearing
144 TODO - should we check for errors in the close operations???
146 if (self
->data
!= NULL
) {
147 UnmapViewOfFile(self
->data
);
150 if (self
->map_handle
!= NULL
) {
151 CloseHandle(self
->map_handle
);
152 self
->map_handle
= NULL
;
154 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
155 CloseHandle(self
->file_handle
);
156 self
->file_handle
= INVALID_HANDLE_VALUE
;
158 #endif /* MS_WINDOWS */
161 (void) close(self
->fd
);
163 if (self
->data
!= NULL
) {
164 munmap(self
->data
, self
->size
);
174 #define CHECK_VALID(err) \
176 if (self->map_handle == NULL) { \
177 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
181 #endif /* MS_WINDOWS */
184 #define CHECK_VALID(err) \
186 if (self->data == NULL) { \
187 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
194 mmap_read_byte_method(mmap_object
*self
,
198 if (self
->pos
< self
->size
) {
199 char value
= self
->data
[self
->pos
];
201 return Py_BuildValue("c", value
);
203 PyErr_SetString(PyExc_ValueError
, "read byte out of range");
209 mmap_read_line_method(mmap_object
*self
,
212 char *start
= self
->data
+self
->pos
;
213 char *eof
= self
->data
+self
->size
;
219 eol
= memchr(start
, '\n', self
->size
- self
->pos
);
223 ++eol
; /* we're interested in the position after the
225 result
= PyString_FromStringAndSize(start
, (eol
- start
));
226 self
->pos
+= (eol
- start
);
231 mmap_read_method(mmap_object
*self
,
234 Py_ssize_t num_bytes
;
238 if (!PyArg_ParseTuple(args
, "n:read", &num_bytes
))
241 /* silently 'adjust' out-of-range requests */
242 if (num_bytes
> self
->size
- self
->pos
) {
243 num_bytes
-= (self
->pos
+num_bytes
) - self
->size
;
245 result
= Py_BuildValue("s#", self
->data
+self
->pos
, num_bytes
);
246 self
->pos
+= num_bytes
;
251 mmap_gfind(mmap_object
*self
,
255 Py_ssize_t start
= self
->pos
;
256 Py_ssize_t end
= self
->size
;
261 if (!PyArg_ParseTuple(args
, reverse
? "s#|nn:rfind" : "s#|nn:find",
262 &needle
, &len
, &start
, &end
)) {
265 const char *p
, *start_p
, *end_p
;
266 int sign
= reverse
? -1 : 1;
272 else if ((size_t)start
> self
->size
)
279 else if ((size_t)end
> self
->size
)
282 start_p
= self
->data
+ start
;
283 end_p
= self
->data
+ end
;
285 for (p
= (reverse
? end_p
- len
: start_p
);
286 (p
>= start_p
) && (p
+ len
<= end_p
); p
+= sign
) {
288 for (i
= 0; i
< len
&& needle
[i
] == p
[i
]; ++i
)
291 return PyInt_FromSsize_t(p
- self
->data
);
294 return PyInt_FromLong(-1);
299 mmap_find_method(mmap_object
*self
,
302 return mmap_gfind(self
, args
, 0);
306 mmap_rfind_method(mmap_object
*self
,
309 return mmap_gfind(self
, args
, 1);
313 is_writeable(mmap_object
*self
)
315 if (self
->access
!= ACCESS_READ
)
317 PyErr_Format(PyExc_TypeError
, "mmap can't modify a readonly memory map.");
322 is_resizeable(mmap_object
*self
)
324 if ((self
->access
== ACCESS_WRITE
) || (self
->access
== ACCESS_DEFAULT
))
326 PyErr_Format(PyExc_TypeError
,
327 "mmap can't resize a readonly or copy-on-write memory map.");
333 mmap_write_method(mmap_object
*self
,
340 if (!PyArg_ParseTuple(args
, "s#:write", &data
, &length
))
343 if (!is_writeable(self
))
346 if ((self
->pos
+ length
) > self
->size
) {
347 PyErr_SetString(PyExc_ValueError
, "data out of range");
350 memcpy(self
->data
+self
->pos
, data
, length
);
351 self
->pos
= self
->pos
+length
;
357 mmap_write_byte_method(mmap_object
*self
,
363 if (!PyArg_ParseTuple(args
, "c:write_byte", &value
))
366 if (!is_writeable(self
))
369 if (self
->pos
< self
->size
) {
370 *(self
->data
+self
->pos
) = value
;
376 PyErr_SetString(PyExc_ValueError
, "write byte out of range");
382 mmap_size_method(mmap_object
*self
,
388 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
391 low
= GetFileSize(self
->file_handle
, &high
);
392 if (low
== INVALID_FILE_SIZE
) {
393 /* It might be that the function appears to have failed,
394 when indeed its size equals INVALID_FILE_SIZE */
395 DWORD error
= GetLastError();
396 if (error
!= NO_ERROR
)
397 return PyErr_SetFromWindowsErr(error
);
399 if (!high
&& low
< LONG_MAX
)
400 return PyInt_FromLong((long)low
);
401 size
= (((PY_LONG_LONG
)high
)<<32) + low
;
402 return PyLong_FromLongLong(size
);
404 return PyInt_FromSsize_t(self
->size
);
406 #endif /* MS_WINDOWS */
411 if (-1 == fstat(self
->fd
, &buf
)) {
412 PyErr_SetFromErrno(mmap_module_error
);
415 return PyInt_FromSsize_t(buf
.st_size
);
420 /* This assumes that you want the entire file mapped,
421 / and when recreating the map will make the new file
424 / Is this really necessary? This could easily be done
425 / from python by just closing and re-opening with the
430 mmap_resize_method(mmap_object
*self
,
435 if (!PyArg_ParseTuple(args
, "n:resize", &new_size
) ||
436 !is_resizeable(self
)) {
441 DWORD off_hi
, off_lo
, newSizeLow
, newSizeHigh
;
442 /* First, unmap the file view */
443 UnmapViewOfFile(self
->data
);
445 /* Close the mapping object */
446 CloseHandle(self
->map_handle
);
447 self
->map_handle
= NULL
;
448 /* Move to the desired EOF position */
449 #if SIZEOF_SIZE_T > 4
450 newSizeHigh
= (DWORD
)((self
->offset
+ new_size
) >> 32);
451 newSizeLow
= (DWORD
)((self
->offset
+ new_size
) & 0xFFFFFFFF);
452 off_hi
= (DWORD
)(self
->offset
>> 32);
453 off_lo
= (DWORD
)(self
->offset
& 0xFFFFFFFF);
456 newSizeLow
= (DWORD
)(self
->offset
+ new_size
);
458 off_lo
= (DWORD
)self
->offset
;
460 SetFilePointer(self
->file_handle
,
461 newSizeLow
, &newSizeHigh
, FILE_BEGIN
);
462 /* Change the size of the file */
463 SetEndOfFile(self
->file_handle
);
464 /* Create another mapping object and remap the file view */
465 self
->map_handle
= CreateFileMapping(
472 if (self
->map_handle
!= NULL
) {
473 self
->data
= (char *) MapViewOfFile(self
->map_handle
,
478 if (self
->data
!= NULL
) {
479 self
->size
= new_size
;
483 dwErrCode
= GetLastError();
484 CloseHandle(self
->map_handle
);
485 self
->map_handle
= NULL
;
488 dwErrCode
= GetLastError();
490 PyErr_SetFromWindowsErr(dwErrCode
);
492 #endif /* MS_WINDOWS */
497 PyErr_SetString(PyExc_SystemError
,
498 "mmap: resizing not available--no mremap()");
504 if (ftruncate(self
->fd
, self
->offset
+ new_size
) == -1) {
505 PyErr_SetFromErrno(mmap_module_error
);
509 #ifdef MREMAP_MAYMOVE
510 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
512 #if defined(__NetBSD__)
513 newmap
= mremap(self
->data
, self
->size
, self
->data
, new_size
, 0);
515 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
516 #endif /* __NetBSD__ */
518 if (newmap
== (void *)-1)
520 PyErr_SetFromErrno(mmap_module_error
);
524 self
->size
= new_size
;
527 #endif /* HAVE_MREMAP */
533 mmap_tell_method(mmap_object
*self
, PyObject
*unused
)
536 return PyInt_FromSize_t(self
->pos
);
540 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
542 Py_ssize_t offset
= 0;
543 Py_ssize_t size
= self
->size
;
545 if (!PyArg_ParseTuple(args
, "|nn:flush", &offset
, &size
))
547 if ((size_t)(offset
+ size
) > self
->size
) {
548 PyErr_SetString(PyExc_ValueError
, "flush values out of range");
552 return PyInt_FromLong((long) FlushViewOfFile(self
->data
+offset
, size
));
554 /* XXX semantics of return value? */
555 /* XXX flags for msync? */
556 if (-1 == msync(self
->data
+ offset
, size
, MS_SYNC
)) {
557 PyErr_SetFromErrno(mmap_module_error
);
560 return PyInt_FromLong(0);
562 PyErr_SetString(PyExc_ValueError
, "flush not supported on this system");
568 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
573 if (!PyArg_ParseTuple(args
, "n|i:seek", &dist
, &how
))
578 case 0: /* relative to start */
583 case 1: /* relative to current position */
584 if ((Py_ssize_t
)self
->pos
+ dist
< 0)
586 where
= self
->pos
+ dist
;
588 case 2: /* relative to end */
589 if ((Py_ssize_t
)self
->size
+ dist
< 0)
591 where
= self
->size
+ dist
;
594 PyErr_SetString(PyExc_ValueError
, "unknown seek type");
597 if (where
> self
->size
)
605 PyErr_SetString(PyExc_ValueError
, "seek out of range");
610 mmap_move_method(mmap_object
*self
, PyObject
*args
)
612 unsigned long dest
, src
, cnt
;
614 if (!PyArg_ParseTuple(args
, "kkk:move", &dest
, &src
, &cnt
) ||
615 !is_writeable(self
)) {
618 /* bounds check the values */
619 if (cnt
< 0 || (cnt
+ dest
) < cnt
|| (cnt
+ src
) < cnt
||
620 src
< 0 || src
> self
->size
|| (src
+ cnt
) > self
->size
||
621 dest
< 0 || dest
> self
->size
|| (dest
+ cnt
) > self
->size
) {
622 PyErr_SetString(PyExc_ValueError
,
623 "source, destination, or count out of range");
626 memmove(self
->data
+dest
, self
->data
+src
, cnt
);
632 static struct PyMethodDef mmap_object_methods
[] = {
633 {"close", (PyCFunction
) mmap_close_method
, METH_NOARGS
},
634 {"find", (PyCFunction
) mmap_find_method
, METH_VARARGS
},
635 {"rfind", (PyCFunction
) mmap_rfind_method
, METH_VARARGS
},
636 {"flush", (PyCFunction
) mmap_flush_method
, METH_VARARGS
},
637 {"move", (PyCFunction
) mmap_move_method
, METH_VARARGS
},
638 {"read", (PyCFunction
) mmap_read_method
, METH_VARARGS
},
639 {"read_byte", (PyCFunction
) mmap_read_byte_method
, METH_NOARGS
},
640 {"readline", (PyCFunction
) mmap_read_line_method
, METH_NOARGS
},
641 {"resize", (PyCFunction
) mmap_resize_method
, METH_VARARGS
},
642 {"seek", (PyCFunction
) mmap_seek_method
, METH_VARARGS
},
643 {"size", (PyCFunction
) mmap_size_method
, METH_NOARGS
},
644 {"tell", (PyCFunction
) mmap_tell_method
, METH_NOARGS
},
645 {"write", (PyCFunction
) mmap_write_method
, METH_VARARGS
},
646 {"write_byte", (PyCFunction
) mmap_write_byte_method
, METH_VARARGS
},
647 {NULL
, NULL
} /* sentinel */
650 /* Functions for treating an mmap'ed file as a buffer */
653 mmap_buffer_getreadbuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
657 PyErr_SetString(PyExc_SystemError
,
658 "Accessing non-existent mmap segment");
666 mmap_buffer_getwritebuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
670 PyErr_SetString(PyExc_SystemError
,
671 "Accessing non-existent mmap segment");
674 if (!is_writeable(self
))
681 mmap_buffer_getsegcount(mmap_object
*self
, Py_ssize_t
*lenp
)
690 mmap_buffer_getcharbuffer(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
693 PyErr_SetString(PyExc_SystemError
,
694 "accessing non-existent buffer segment");
697 *ptr
= (const char *)self
->data
;
702 mmap_length(mmap_object
*self
)
709 mmap_item(mmap_object
*self
, Py_ssize_t i
)
712 if (i
< 0 || (size_t)i
>= self
->size
) {
713 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
716 return PyString_FromStringAndSize(self
->data
+ i
, 1);
720 mmap_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
)
725 else if ((size_t)ilow
> self
->size
)
731 else if ((size_t)ihigh
> self
->size
)
734 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
738 mmap_subscript(mmap_object
*self
, PyObject
*item
)
741 if (PyIndex_Check(item
)) {
742 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
743 if (i
== -1 && PyErr_Occurred())
747 if (i
< 0 || (size_t)i
>= self
->size
) {
748 PyErr_SetString(PyExc_IndexError
,
749 "mmap index out of range");
752 return PyString_FromStringAndSize(self
->data
+ i
, 1);
754 else if (PySlice_Check(item
)) {
755 Py_ssize_t start
, stop
, step
, slicelen
;
757 if (PySlice_GetIndicesEx((PySliceObject
*)item
, self
->size
,
758 &start
, &stop
, &step
, &slicelen
) < 0) {
763 return PyString_FromStringAndSize("", 0);
765 return PyString_FromStringAndSize(self
->data
+ start
,
768 char *result_buf
= (char *)PyMem_Malloc(slicelen
);
772 if (result_buf
== NULL
)
773 return PyErr_NoMemory();
774 for (cur
= start
, i
= 0; i
< slicelen
;
776 result_buf
[i
] = self
->data
[cur
];
778 result
= PyString_FromStringAndSize(result_buf
,
780 PyMem_Free(result_buf
);
785 PyErr_SetString(PyExc_TypeError
,
786 "mmap indices must be integers");
792 mmap_concat(mmap_object
*self
, PyObject
*bb
)
795 PyErr_SetString(PyExc_SystemError
,
796 "mmaps don't support concatenation");
801 mmap_repeat(mmap_object
*self
, Py_ssize_t n
)
804 PyErr_SetString(PyExc_SystemError
,
805 "mmaps don't support repeat operation");
810 mmap_ass_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
, PyObject
*v
)
817 else if ((size_t)ilow
> self
->size
)
823 else if ((size_t)ihigh
> self
->size
)
827 PyErr_SetString(PyExc_TypeError
,
828 "mmap object doesn't support slice deletion");
831 if (! (PyString_Check(v
)) ) {
832 PyErr_SetString(PyExc_IndexError
,
833 "mmap slice assignment must be a string");
836 if (PyString_Size(v
) != (ihigh
- ilow
)) {
837 PyErr_SetString(PyExc_IndexError
,
838 "mmap slice assignment is wrong size");
841 if (!is_writeable(self
))
843 buf
= PyString_AsString(v
);
844 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
849 mmap_ass_item(mmap_object
*self
, Py_ssize_t i
, PyObject
*v
)
854 if (i
< 0 || (size_t)i
>= self
->size
) {
855 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
859 PyErr_SetString(PyExc_TypeError
,
860 "mmap object doesn't support item deletion");
863 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
864 PyErr_SetString(PyExc_IndexError
,
865 "mmap assignment must be single-character string");
868 if (!is_writeable(self
))
870 buf
= PyString_AsString(v
);
871 self
->data
[i
] = buf
[0];
876 mmap_ass_subscript(mmap_object
*self
, PyObject
*item
, PyObject
*value
)
880 if (PyIndex_Check(item
)) {
881 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
884 if (i
== -1 && PyErr_Occurred())
888 if (i
< 0 || (size_t)i
>= self
->size
) {
889 PyErr_SetString(PyExc_IndexError
,
890 "mmap index out of range");
894 PyErr_SetString(PyExc_TypeError
,
895 "mmap object doesn't support item deletion");
898 if (!PyString_Check(value
) || PyString_Size(value
) != 1) {
899 PyErr_SetString(PyExc_IndexError
,
900 "mmap assignment must be single-character string");
903 if (!is_writeable(self
))
905 buf
= PyString_AsString(value
);
906 self
->data
[i
] = buf
[0];
909 else if (PySlice_Check(item
)) {
910 Py_ssize_t start
, stop
, step
, slicelen
;
912 if (PySlice_GetIndicesEx((PySliceObject
*)item
,
913 self
->size
, &start
, &stop
,
914 &step
, &slicelen
) < 0) {
918 PyErr_SetString(PyExc_TypeError
,
919 "mmap object doesn't support slice deletion");
922 if (!PyString_Check(value
)) {
923 PyErr_SetString(PyExc_IndexError
,
924 "mmap slice assignment must be a string");
927 if (PyString_Size(value
) != slicelen
) {
928 PyErr_SetString(PyExc_IndexError
,
929 "mmap slice assignment is wrong size");
932 if (!is_writeable(self
))
937 else if (step
== 1) {
938 const char *buf
= PyString_AsString(value
);
942 memcpy(self
->data
+ start
, buf
, slicelen
);
947 const char *buf
= PyString_AsString(value
);
951 for (cur
= start
, i
= 0; i
< slicelen
;
953 self
->data
[cur
] = buf
[i
];
959 PyErr_SetString(PyExc_TypeError
,
960 "mmap indices must be integer");
965 static PySequenceMethods mmap_as_sequence
= {
966 (lenfunc
)mmap_length
, /*sq_length*/
967 (binaryfunc
)mmap_concat
, /*sq_concat*/
968 (ssizeargfunc
)mmap_repeat
, /*sq_repeat*/
969 (ssizeargfunc
)mmap_item
, /*sq_item*/
970 (ssizessizeargfunc
)mmap_slice
, /*sq_slice*/
971 (ssizeobjargproc
)mmap_ass_item
, /*sq_ass_item*/
972 (ssizessizeobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
975 static PyMappingMethods mmap_as_mapping
= {
976 (lenfunc
)mmap_length
,
977 (binaryfunc
)mmap_subscript
,
978 (objobjargproc
)mmap_ass_subscript
,
981 static PyBufferProcs mmap_as_buffer
= {
982 (readbufferproc
)mmap_buffer_getreadbuf
,
983 (writebufferproc
)mmap_buffer_getwritebuf
,
984 (segcountproc
)mmap_buffer_getsegcount
,
985 (charbufferproc
)mmap_buffer_getcharbuffer
,
989 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
);
991 PyDoc_STRVAR(mmap_doc
,
992 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
994 Maps length bytes from the file specified by the file handle fileno,\n\
995 and returns a mmap object. If length is larger than the current size\n\
996 of the file, the file is extended to contain length bytes. If length\n\
997 is 0, the maximum length of the map is the current size of the file,\n\
998 except that if the file is empty Windows raises an exception (you cannot\n\
999 create an empty mapping on Windows).\n\
1001 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1003 Maps length bytes from the file specified by the file descriptor fileno,\n\
1004 and returns a mmap object. If length is 0, the maximum length of the map\n\
1005 will be the current size of the file when mmap is called.\n\
1006 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1007 private copy-on-write mapping, so changes to the contents of the mmap\n\
1008 object will be private to this process, and MAP_SHARED creates a mapping\n\
1009 that's shared with all other processes mapping the same areas of the file.\n\
1010 The default value is MAP_SHARED.\n\
1012 To map anonymous memory, pass -1 as the fileno (both versions).");
1015 static PyTypeObject mmap_object_type
= {
1016 PyVarObject_HEAD_INIT(NULL
, 0)
1017 "mmap.mmap", /* tp_name */
1018 sizeof(mmap_object
), /* tp_size */
1019 0, /* tp_itemsize */
1021 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
1027 0, /* tp_as_number */
1028 &mmap_as_sequence
, /*tp_as_sequence*/
1029 &mmap_as_mapping
, /*tp_as_mapping*/
1033 PyObject_GenericGetAttr
, /*tp_getattro*/
1035 &mmap_as_buffer
, /*tp_as_buffer*/
1036 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
1037 mmap_doc
, /*tp_doc*/
1038 0, /* tp_traverse */
1040 0, /* tp_richcompare */
1041 0, /* tp_weaklistoffset */
1043 0, /* tp_iternext */
1044 mmap_object_methods
, /* tp_methods */
1049 0, /* tp_descr_get */
1050 0, /* tp_descr_set */
1051 0, /* tp_dictoffset */
1053 PyType_GenericAlloc
, /* tp_alloc */
1054 new_mmap_object
, /* tp_new */
1055 PyObject_Del
, /* tp_free */
1059 /* extract the map size from the given PyObject
1061 Returns -1 on error, with an appropriate Python exception raised. On
1062 success, the map size is returned. */
1064 _GetMapSize(PyObject
*o
, const char* param
)
1068 if (PyIndex_Check(o
)) {
1069 Py_ssize_t i
= PyNumber_AsSsize_t(o
, PyExc_OverflowError
);
1070 if (i
==-1 && PyErr_Occurred())
1073 PyErr_Format(PyExc_OverflowError
,
1074 "memory mapped %s must be positive",
1081 PyErr_SetString(PyExc_TypeError
, "map size must be an integral value");
1087 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
)
1093 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1094 Py_ssize_t map_size
, offset
;
1095 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
1097 int access
= (int)ACCESS_DEFAULT
;
1098 static char *keywords
[] = {"fileno", "length",
1100 "access", "offset", NULL
};
1102 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|iiiO", keywords
,
1103 &fd
, &map_size_obj
, &flags
, &prot
,
1104 &access
, &offset_obj
))
1106 map_size
= _GetMapSize(map_size_obj
, "size");
1109 offset
= _GetMapSize(offset_obj
, "offset");
1113 if ((access
!= (int)ACCESS_DEFAULT
) &&
1114 ((flags
!= MAP_SHARED
) || (prot
!= (PROT_WRITE
| PROT_READ
))))
1115 return PyErr_Format(PyExc_ValueError
,
1116 "mmap can't specify both access and flags, prot.");
1117 switch ((access_mode
)access
) {
1124 prot
= PROT_READ
| PROT_WRITE
;
1127 flags
= MAP_PRIVATE
;
1128 prot
= PROT_READ
| PROT_WRITE
;
1130 case ACCESS_DEFAULT
:
1131 /* use the specified or default values of flags and prot */
1134 return PyErr_Format(PyExc_ValueError
,
1135 "mmap invalid access parameter.");
1138 if (prot
== PROT_READ
) {
1139 access
= ACCESS_READ
;
1144 /* on OpenVMS we must ensure that all bytes are written to the file */
1149 if (fd
!= -1 && fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
1150 if (map_size
== 0) {
1151 map_size
= st
.st_size
;
1152 } else if ((size_t)offset
+ (size_t)map_size
> st
.st_size
) {
1153 PyErr_SetString(PyExc_ValueError
,
1154 "mmap length is greater than file size");
1159 m_obj
= (mmap_object
*)type
->tp_alloc(type
, 0);
1160 if (m_obj
== NULL
) {return NULL
;}
1162 m_obj
->size
= (size_t) map_size
;
1163 m_obj
->pos
= (size_t) 0;
1164 m_obj
->offset
= offset
;
1167 /* Assume the caller wants to map anonymous memory.
1168 This is the same behaviour as Windows. mmap.mmap(-1, size)
1169 on both Windows and Unix map anonymous memory.
1171 #ifdef MAP_ANONYMOUS
1172 /* BSD way to map anonymous memory */
1173 flags
|= MAP_ANONYMOUS
;
1175 /* SVR4 method to map anonymous memory is to open /dev/zero */
1176 fd
= devzero
= open("/dev/zero", O_RDWR
);
1177 if (devzero
== -1) {
1179 PyErr_SetFromErrno(mmap_module_error
);
1184 m_obj
->fd
= dup(fd
);
1185 if (m_obj
->fd
== -1) {
1187 PyErr_SetFromErrno(mmap_module_error
);
1192 m_obj
->data
= mmap(NULL
, map_size
,
1196 if (devzero
!= -1) {
1200 if (m_obj
->data
== (char *)-1) {
1203 PyErr_SetFromErrno(mmap_module_error
);
1206 m_obj
->access
= (access_mode
)access
;
1207 return (PyObject
*)m_obj
;
1213 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
)
1216 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1217 Py_ssize_t map_size
, offset
;
1218 DWORD off_hi
; /* upper 32 bits of offset */
1219 DWORD off_lo
; /* lower 32 bits of offset */
1220 DWORD size_hi
; /* upper 32 bits of size */
1221 DWORD size_lo
; /* lower 32 bits of size */
1226 int access
= (access_mode
)ACCESS_DEFAULT
;
1227 DWORD flProtect
, dwDesiredAccess
;
1228 static char *keywords
[] = { "fileno", "length",
1230 "access", "offset", NULL
};
1232 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|ziO", keywords
,
1233 &fileno
, &map_size_obj
,
1234 &tagname
, &access
, &offset_obj
)) {
1238 switch((access_mode
)access
) {
1240 flProtect
= PAGE_READONLY
;
1241 dwDesiredAccess
= FILE_MAP_READ
;
1243 case ACCESS_DEFAULT
: case ACCESS_WRITE
:
1244 flProtect
= PAGE_READWRITE
;
1245 dwDesiredAccess
= FILE_MAP_WRITE
;
1248 flProtect
= PAGE_WRITECOPY
;
1249 dwDesiredAccess
= FILE_MAP_COPY
;
1252 return PyErr_Format(PyExc_ValueError
,
1253 "mmap invalid access parameter.");
1256 map_size
= _GetMapSize(map_size_obj
, "size");
1259 offset
= _GetMapSize(offset_obj
, "offset");
1263 /* assume -1 and 0 both mean invalid filedescriptor
1264 to 'anonymously' map memory.
1265 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1266 XXX: Should this code be added?
1268 PyErr_Warn(PyExc_DeprecationWarning,
1269 "don't use 0 for anonymous memory");
1271 if (fileno
!= -1 && fileno
!= 0) {
1272 fh
= (HANDLE
)_get_osfhandle(fileno
);
1273 if (fh
==(HANDLE
)-1) {
1274 PyErr_SetFromErrno(mmap_module_error
);
1277 /* Win9x appears to need us seeked to zero */
1278 lseek(fileno
, 0, SEEK_SET
);
1281 m_obj
= (mmap_object
*)type
->tp_alloc(type
, 0);
1284 /* Set every field to an invalid marker, so we can safely
1285 destruct the object in the face of failure */
1287 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
1288 m_obj
->map_handle
= NULL
;
1289 m_obj
->tagname
= NULL
;
1290 m_obj
->offset
= offset
;
1293 /* It is necessary to duplicate the handle, so the
1294 Python code can close it on us */
1295 if (!DuplicateHandle(
1296 GetCurrentProcess(), /* source process handle */
1297 fh
, /* handle to be duplicated */
1298 GetCurrentProcess(), /* target proc handle */
1299 (LPHANDLE
)&m_obj
->file_handle
, /* result */
1300 0, /* access - ignored due to options value */
1301 FALSE
, /* inherited by child processes? */
1302 DUPLICATE_SAME_ACCESS
)) { /* options */
1303 dwErr
= GetLastError();
1305 PyErr_SetFromWindowsErr(dwErr
);
1310 low
= GetFileSize(fh
, &high
);
1311 /* low might just happen to have the value INVALID_FILE_SIZE;
1312 so we need to check the last error also. */
1313 if (low
== INVALID_FILE_SIZE
&&
1314 (dwErr
= GetLastError()) != NO_ERROR
) {
1316 return PyErr_SetFromWindowsErr(dwErr
);
1319 #if SIZEOF_SIZE_T > 4
1320 m_obj
->size
= (((size_t)high
)<<32) + low
;
1323 /* File is too large to map completely */
1324 m_obj
->size
= (size_t)-1;
1329 m_obj
->size
= map_size
;
1333 m_obj
->size
= map_size
;
1336 /* set the initial position */
1337 m_obj
->pos
= (size_t) 0;
1339 /* set the tag name */
1340 if (tagname
!= NULL
&& *tagname
!= '\0') {
1341 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
1342 if (m_obj
->tagname
== NULL
) {
1347 strcpy(m_obj
->tagname
, tagname
);
1350 m_obj
->tagname
= NULL
;
1352 m_obj
->access
= (access_mode
)access
;
1353 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1354 * more than 4 bytes, we need to break it apart. Else (size_t
1355 * consumes 4 bytes), C doesn't define what happens if we shift
1356 * right by 32, so we need different code.
1358 #if SIZEOF_SIZE_T > 4
1359 size_hi
= (DWORD
)((offset
+ m_obj
->size
) >> 32);
1360 size_lo
= (DWORD
)((offset
+ m_obj
->size
) & 0xFFFFFFFF);
1361 off_hi
= (DWORD
)(offset
>> 32);
1362 off_lo
= (DWORD
)(offset
& 0xFFFFFFFF);
1365 size_lo
= (DWORD
)(offset
+ m_obj
->size
);
1367 off_lo
= (DWORD
)offset
;
1369 /* For files, it would be sufficient to pass 0 as size.
1370 For anonymous maps, we have to pass the size explicitly. */
1371 m_obj
->map_handle
= CreateFileMapping(m_obj
->file_handle
,
1377 if (m_obj
->map_handle
!= NULL
) {
1378 m_obj
->data
= (char *) MapViewOfFile(m_obj
->map_handle
,
1383 if (m_obj
->data
!= NULL
)
1384 return (PyObject
*)m_obj
;
1386 dwErr
= GetLastError();
1387 CloseHandle(m_obj
->map_handle
);
1388 m_obj
->map_handle
= NULL
;
1391 dwErr
= GetLastError();
1393 PyErr_SetFromWindowsErr(dwErr
);
1396 #endif /* MS_WINDOWS */
1399 setint(PyObject
*d
, const char *name
, long value
)
1401 PyObject
*o
= PyInt_FromLong(value
);
1402 if (o
&& PyDict_SetItemString(d
, name
, o
) == 0) {
1410 PyObject
*dict
, *module
;
1412 if (PyType_Ready(&mmap_object_type
) < 0)
1415 module
= Py_InitModule("mmap", NULL
);
1418 dict
= PyModule_GetDict(module
);
1421 mmap_module_error
= PyErr_NewException("mmap.error",
1422 PyExc_EnvironmentError
, NULL
);
1423 if (mmap_module_error
== NULL
)
1425 PyDict_SetItemString(dict
, "error", mmap_module_error
);
1426 PyDict_SetItemString(dict
, "mmap", (PyObject
*) &mmap_object_type
);
1428 setint(dict
, "PROT_EXEC", PROT_EXEC
);
1431 setint(dict
, "PROT_READ", PROT_READ
);
1434 setint(dict
, "PROT_WRITE", PROT_WRITE
);
1438 setint(dict
, "MAP_SHARED", MAP_SHARED
);
1441 setint(dict
, "MAP_PRIVATE", MAP_PRIVATE
);
1443 #ifdef MAP_DENYWRITE
1444 setint(dict
, "MAP_DENYWRITE", MAP_DENYWRITE
);
1446 #ifdef MAP_EXECUTABLE
1447 setint(dict
, "MAP_EXECUTABLE", MAP_EXECUTABLE
);
1449 #ifdef MAP_ANONYMOUS
1450 setint(dict
, "MAP_ANON", MAP_ANONYMOUS
);
1451 setint(dict
, "MAP_ANONYMOUS", MAP_ANONYMOUS
);
1454 setint(dict
, "PAGESIZE", (long)my_getpagesize());
1456 setint(dict
, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1458 setint(dict
, "ACCESS_READ", ACCESS_READ
);
1459 setint(dict
, "ACCESS_WRITE", ACCESS_WRITE
);
1460 setint(dict
, "ACCESS_COPY", ACCESS_COPY
);