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 */
162 (void) close(self
->fd
);
164 if (self
->data
!= NULL
) {
165 munmap(self
->data
, self
->size
);
175 #define CHECK_VALID(err) \
177 if (self->map_handle == NULL) { \
178 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
182 #endif /* MS_WINDOWS */
185 #define CHECK_VALID(err) \
187 if (self->data == NULL) { \
188 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
195 mmap_read_byte_method(mmap_object
*self
,
199 if (self
->pos
< self
->size
) {
200 char value
= self
->data
[self
->pos
];
202 return Py_BuildValue("c", value
);
204 PyErr_SetString(PyExc_ValueError
, "read byte out of range");
210 mmap_read_line_method(mmap_object
*self
,
213 char *start
= self
->data
+self
->pos
;
214 char *eof
= self
->data
+self
->size
;
220 eol
= memchr(start
, '\n', self
->size
- self
->pos
);
224 ++eol
; /* we're interested in the position after the
226 result
= PyString_FromStringAndSize(start
, (eol
- start
));
227 self
->pos
+= (eol
- start
);
232 mmap_read_method(mmap_object
*self
,
235 Py_ssize_t num_bytes
;
239 if (!PyArg_ParseTuple(args
, "n:read", &num_bytes
))
242 /* silently 'adjust' out-of-range requests */
243 if (num_bytes
> self
->size
- self
->pos
) {
244 num_bytes
-= (self
->pos
+num_bytes
) - self
->size
;
246 result
= Py_BuildValue("s#", self
->data
+self
->pos
, num_bytes
);
247 self
->pos
+= num_bytes
;
252 mmap_gfind(mmap_object
*self
,
256 Py_ssize_t start
= self
->pos
;
257 Py_ssize_t end
= self
->size
;
262 if (!PyArg_ParseTuple(args
, reverse
? "s#|nn:rfind" : "s#|nn:find",
263 &needle
, &len
, &start
, &end
)) {
266 const char *p
, *start_p
, *end_p
;
267 int sign
= reverse
? -1 : 1;
273 else if ((size_t)start
> self
->size
)
280 else if ((size_t)end
> self
->size
)
283 start_p
= self
->data
+ start
;
284 end_p
= self
->data
+ end
;
286 for (p
= (reverse
? end_p
- len
: start_p
);
287 (p
>= start_p
) && (p
+ len
<= end_p
); p
+= sign
) {
289 for (i
= 0; i
< len
&& needle
[i
] == p
[i
]; ++i
)
292 return PyInt_FromSsize_t(p
- self
->data
);
295 return PyInt_FromLong(-1);
300 mmap_find_method(mmap_object
*self
,
303 return mmap_gfind(self
, args
, 0);
307 mmap_rfind_method(mmap_object
*self
,
310 return mmap_gfind(self
, args
, 1);
314 is_writeable(mmap_object
*self
)
316 if (self
->access
!= ACCESS_READ
)
318 PyErr_Format(PyExc_TypeError
, "mmap can't modify a readonly memory map.");
323 is_resizeable(mmap_object
*self
)
325 if ((self
->access
== ACCESS_WRITE
) || (self
->access
== ACCESS_DEFAULT
))
327 PyErr_Format(PyExc_TypeError
,
328 "mmap can't resize a readonly or copy-on-write memory map.");
334 mmap_write_method(mmap_object
*self
,
341 if (!PyArg_ParseTuple(args
, "s#:write", &data
, &length
))
344 if (!is_writeable(self
))
347 if ((self
->pos
+ length
) > self
->size
) {
348 PyErr_SetString(PyExc_ValueError
, "data out of range");
351 memcpy(self
->data
+self
->pos
, data
, length
);
352 self
->pos
= self
->pos
+length
;
358 mmap_write_byte_method(mmap_object
*self
,
364 if (!PyArg_ParseTuple(args
, "c:write_byte", &value
))
367 if (!is_writeable(self
))
370 if (self
->pos
< self
->size
) {
371 *(self
->data
+self
->pos
) = value
;
377 PyErr_SetString(PyExc_ValueError
, "write byte out of range");
383 mmap_size_method(mmap_object
*self
,
389 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
392 low
= GetFileSize(self
->file_handle
, &high
);
393 if (low
== INVALID_FILE_SIZE
) {
394 /* It might be that the function appears to have failed,
395 when indeed its size equals INVALID_FILE_SIZE */
396 DWORD error
= GetLastError();
397 if (error
!= NO_ERROR
)
398 return PyErr_SetFromWindowsErr(error
);
400 if (!high
&& low
< LONG_MAX
)
401 return PyInt_FromLong((long)low
);
402 size
= (((PY_LONG_LONG
)high
)<<32) + low
;
403 return PyLong_FromLongLong(size
);
405 return PyInt_FromSsize_t(self
->size
);
407 #endif /* MS_WINDOWS */
412 if (-1 == fstat(self
->fd
, &buf
)) {
413 PyErr_SetFromErrno(mmap_module_error
);
416 return PyInt_FromSsize_t(buf
.st_size
);
421 /* This assumes that you want the entire file mapped,
422 / and when recreating the map will make the new file
425 / Is this really necessary? This could easily be done
426 / from python by just closing and re-opening with the
431 mmap_resize_method(mmap_object
*self
,
436 if (!PyArg_ParseTuple(args
, "n:resize", &new_size
) ||
437 !is_resizeable(self
)) {
442 DWORD off_hi
, off_lo
, newSizeLow
, newSizeHigh
;
443 /* First, unmap the file view */
444 UnmapViewOfFile(self
->data
);
446 /* Close the mapping object */
447 CloseHandle(self
->map_handle
);
448 self
->map_handle
= NULL
;
449 /* Move to the desired EOF position */
450 #if SIZEOF_SIZE_T > 4
451 newSizeHigh
= (DWORD
)((self
->offset
+ new_size
) >> 32);
452 newSizeLow
= (DWORD
)((self
->offset
+ new_size
) & 0xFFFFFFFF);
453 off_hi
= (DWORD
)(self
->offset
>> 32);
454 off_lo
= (DWORD
)(self
->offset
& 0xFFFFFFFF);
457 newSizeLow
= (DWORD
)(self
->offset
+ new_size
);
459 off_lo
= (DWORD
)self
->offset
;
461 SetFilePointer(self
->file_handle
,
462 newSizeLow
, &newSizeHigh
, FILE_BEGIN
);
463 /* Change the size of the file */
464 SetEndOfFile(self
->file_handle
);
465 /* Create another mapping object and remap the file view */
466 self
->map_handle
= CreateFileMapping(
473 if (self
->map_handle
!= NULL
) {
474 self
->data
= (char *) MapViewOfFile(self
->map_handle
,
479 if (self
->data
!= NULL
) {
480 self
->size
= new_size
;
484 dwErrCode
= GetLastError();
485 CloseHandle(self
->map_handle
);
486 self
->map_handle
= NULL
;
489 dwErrCode
= GetLastError();
491 PyErr_SetFromWindowsErr(dwErrCode
);
493 #endif /* MS_WINDOWS */
498 PyErr_SetString(PyExc_SystemError
,
499 "mmap: resizing not available--no mremap()");
505 if (ftruncate(self
->fd
, self
->offset
+ new_size
) == -1) {
506 PyErr_SetFromErrno(mmap_module_error
);
510 #ifdef MREMAP_MAYMOVE
511 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
513 #if defined(__NetBSD__)
514 newmap
= mremap(self
->data
, self
->size
, self
->data
, new_size
, 0);
516 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
517 #endif /* __NetBSD__ */
519 if (newmap
== (void *)-1)
521 PyErr_SetFromErrno(mmap_module_error
);
525 self
->size
= new_size
;
528 #endif /* HAVE_MREMAP */
534 mmap_tell_method(mmap_object
*self
, PyObject
*unused
)
537 return PyInt_FromSize_t(self
->pos
);
541 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
543 Py_ssize_t offset
= 0;
544 Py_ssize_t size
= self
->size
;
546 if (!PyArg_ParseTuple(args
, "|nn:flush", &offset
, &size
))
548 if ((size_t)(offset
+ size
) > self
->size
) {
549 PyErr_SetString(PyExc_ValueError
, "flush values out of range");
553 return PyInt_FromLong((long) FlushViewOfFile(self
->data
+offset
, size
));
555 /* XXX semantics of return value? */
556 /* XXX flags for msync? */
557 if (-1 == msync(self
->data
+ offset
, size
, MS_SYNC
)) {
558 PyErr_SetFromErrno(mmap_module_error
);
561 return PyInt_FromLong(0);
563 PyErr_SetString(PyExc_ValueError
, "flush not supported on this system");
569 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
574 if (!PyArg_ParseTuple(args
, "n|i:seek", &dist
, &how
))
579 case 0: /* relative to start */
584 case 1: /* relative to current position */
585 if ((Py_ssize_t
)self
->pos
+ dist
< 0)
587 where
= self
->pos
+ dist
;
589 case 2: /* relative to end */
590 if ((Py_ssize_t
)self
->size
+ dist
< 0)
592 where
= self
->size
+ dist
;
595 PyErr_SetString(PyExc_ValueError
, "unknown seek type");
598 if (where
> self
->size
)
606 PyErr_SetString(PyExc_ValueError
, "seek out of range");
611 mmap_move_method(mmap_object
*self
, PyObject
*args
)
613 unsigned long dest
, src
, cnt
;
615 if (!PyArg_ParseTuple(args
, "kkk:move", &dest
, &src
, &cnt
) ||
616 !is_writeable(self
)) {
619 /* bounds check the values */
620 if (cnt
< 0 || (cnt
+ dest
) < cnt
|| (cnt
+ src
) < cnt
||
621 src
< 0 || src
> self
->size
|| (src
+ cnt
) > self
->size
||
622 dest
< 0 || dest
> self
->size
|| (dest
+ cnt
) > self
->size
) {
623 PyErr_SetString(PyExc_ValueError
,
624 "source, destination, or count out of range");
627 memmove(self
->data
+dest
, self
->data
+src
, cnt
);
633 static struct PyMethodDef mmap_object_methods
[] = {
634 {"close", (PyCFunction
) mmap_close_method
, METH_NOARGS
},
635 {"find", (PyCFunction
) mmap_find_method
, METH_VARARGS
},
636 {"rfind", (PyCFunction
) mmap_rfind_method
, METH_VARARGS
},
637 {"flush", (PyCFunction
) mmap_flush_method
, METH_VARARGS
},
638 {"move", (PyCFunction
) mmap_move_method
, METH_VARARGS
},
639 {"read", (PyCFunction
) mmap_read_method
, METH_VARARGS
},
640 {"read_byte", (PyCFunction
) mmap_read_byte_method
, METH_NOARGS
},
641 {"readline", (PyCFunction
) mmap_read_line_method
, METH_NOARGS
},
642 {"resize", (PyCFunction
) mmap_resize_method
, METH_VARARGS
},
643 {"seek", (PyCFunction
) mmap_seek_method
, METH_VARARGS
},
644 {"size", (PyCFunction
) mmap_size_method
, METH_NOARGS
},
645 {"tell", (PyCFunction
) mmap_tell_method
, METH_NOARGS
},
646 {"write", (PyCFunction
) mmap_write_method
, METH_VARARGS
},
647 {"write_byte", (PyCFunction
) mmap_write_byte_method
, METH_VARARGS
},
648 {NULL
, NULL
} /* sentinel */
651 /* Functions for treating an mmap'ed file as a buffer */
654 mmap_buffer_getreadbuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
658 PyErr_SetString(PyExc_SystemError
,
659 "Accessing non-existent mmap segment");
667 mmap_buffer_getwritebuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
671 PyErr_SetString(PyExc_SystemError
,
672 "Accessing non-existent mmap segment");
675 if (!is_writeable(self
))
682 mmap_buffer_getsegcount(mmap_object
*self
, Py_ssize_t
*lenp
)
691 mmap_buffer_getcharbuffer(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
694 PyErr_SetString(PyExc_SystemError
,
695 "accessing non-existent buffer segment");
698 *ptr
= (const char *)self
->data
;
703 mmap_length(mmap_object
*self
)
710 mmap_item(mmap_object
*self
, Py_ssize_t i
)
713 if (i
< 0 || (size_t)i
>= self
->size
) {
714 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
717 return PyString_FromStringAndSize(self
->data
+ i
, 1);
721 mmap_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
)
726 else if ((size_t)ilow
> self
->size
)
732 else if ((size_t)ihigh
> self
->size
)
735 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
739 mmap_subscript(mmap_object
*self
, PyObject
*item
)
742 if (PyIndex_Check(item
)) {
743 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
744 if (i
== -1 && PyErr_Occurred())
748 if (i
< 0 || (size_t)i
>= self
->size
) {
749 PyErr_SetString(PyExc_IndexError
,
750 "mmap index out of range");
753 return PyString_FromStringAndSize(self
->data
+ i
, 1);
755 else if (PySlice_Check(item
)) {
756 Py_ssize_t start
, stop
, step
, slicelen
;
758 if (PySlice_GetIndicesEx((PySliceObject
*)item
, self
->size
,
759 &start
, &stop
, &step
, &slicelen
) < 0) {
764 return PyString_FromStringAndSize("", 0);
766 return PyString_FromStringAndSize(self
->data
+ start
,
769 char *result_buf
= (char *)PyMem_Malloc(slicelen
);
773 if (result_buf
== NULL
)
774 return PyErr_NoMemory();
775 for (cur
= start
, i
= 0; i
< slicelen
;
777 result_buf
[i
] = self
->data
[cur
];
779 result
= PyString_FromStringAndSize(result_buf
,
781 PyMem_Free(result_buf
);
786 PyErr_SetString(PyExc_TypeError
,
787 "mmap indices must be integers");
793 mmap_concat(mmap_object
*self
, PyObject
*bb
)
796 PyErr_SetString(PyExc_SystemError
,
797 "mmaps don't support concatenation");
802 mmap_repeat(mmap_object
*self
, Py_ssize_t n
)
805 PyErr_SetString(PyExc_SystemError
,
806 "mmaps don't support repeat operation");
811 mmap_ass_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
, PyObject
*v
)
818 else if ((size_t)ilow
> self
->size
)
824 else if ((size_t)ihigh
> self
->size
)
828 PyErr_SetString(PyExc_TypeError
,
829 "mmap object doesn't support slice deletion");
832 if (! (PyString_Check(v
)) ) {
833 PyErr_SetString(PyExc_IndexError
,
834 "mmap slice assignment must be a string");
837 if (PyString_Size(v
) != (ihigh
- ilow
)) {
838 PyErr_SetString(PyExc_IndexError
,
839 "mmap slice assignment is wrong size");
842 if (!is_writeable(self
))
844 buf
= PyString_AsString(v
);
845 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
850 mmap_ass_item(mmap_object
*self
, Py_ssize_t i
, PyObject
*v
)
855 if (i
< 0 || (size_t)i
>= self
->size
) {
856 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
860 PyErr_SetString(PyExc_TypeError
,
861 "mmap object doesn't support item deletion");
864 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
865 PyErr_SetString(PyExc_IndexError
,
866 "mmap assignment must be single-character string");
869 if (!is_writeable(self
))
871 buf
= PyString_AsString(v
);
872 self
->data
[i
] = buf
[0];
877 mmap_ass_subscript(mmap_object
*self
, PyObject
*item
, PyObject
*value
)
881 if (PyIndex_Check(item
)) {
882 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
885 if (i
== -1 && PyErr_Occurred())
889 if (i
< 0 || (size_t)i
>= self
->size
) {
890 PyErr_SetString(PyExc_IndexError
,
891 "mmap index out of range");
895 PyErr_SetString(PyExc_TypeError
,
896 "mmap object doesn't support item deletion");
899 if (!PyString_Check(value
) || PyString_Size(value
) != 1) {
900 PyErr_SetString(PyExc_IndexError
,
901 "mmap assignment must be single-character string");
904 if (!is_writeable(self
))
906 buf
= PyString_AsString(value
);
907 self
->data
[i
] = buf
[0];
910 else if (PySlice_Check(item
)) {
911 Py_ssize_t start
, stop
, step
, slicelen
;
913 if (PySlice_GetIndicesEx((PySliceObject
*)item
,
914 self
->size
, &start
, &stop
,
915 &step
, &slicelen
) < 0) {
919 PyErr_SetString(PyExc_TypeError
,
920 "mmap object doesn't support slice deletion");
923 if (!PyString_Check(value
)) {
924 PyErr_SetString(PyExc_IndexError
,
925 "mmap slice assignment must be a string");
928 if (PyString_Size(value
) != slicelen
) {
929 PyErr_SetString(PyExc_IndexError
,
930 "mmap slice assignment is wrong size");
933 if (!is_writeable(self
))
938 else if (step
== 1) {
939 const char *buf
= PyString_AsString(value
);
943 memcpy(self
->data
+ start
, buf
, slicelen
);
948 const char *buf
= PyString_AsString(value
);
952 for (cur
= start
, i
= 0; i
< slicelen
;
954 self
->data
[cur
] = buf
[i
];
960 PyErr_SetString(PyExc_TypeError
,
961 "mmap indices must be integer");
966 static PySequenceMethods mmap_as_sequence
= {
967 (lenfunc
)mmap_length
, /*sq_length*/
968 (binaryfunc
)mmap_concat
, /*sq_concat*/
969 (ssizeargfunc
)mmap_repeat
, /*sq_repeat*/
970 (ssizeargfunc
)mmap_item
, /*sq_item*/
971 (ssizessizeargfunc
)mmap_slice
, /*sq_slice*/
972 (ssizeobjargproc
)mmap_ass_item
, /*sq_ass_item*/
973 (ssizessizeobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
976 static PyMappingMethods mmap_as_mapping
= {
977 (lenfunc
)mmap_length
,
978 (binaryfunc
)mmap_subscript
,
979 (objobjargproc
)mmap_ass_subscript
,
982 static PyBufferProcs mmap_as_buffer
= {
983 (readbufferproc
)mmap_buffer_getreadbuf
,
984 (writebufferproc
)mmap_buffer_getwritebuf
,
985 (segcountproc
)mmap_buffer_getsegcount
,
986 (charbufferproc
)mmap_buffer_getcharbuffer
,
990 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
);
992 PyDoc_STRVAR(mmap_doc
,
993 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
995 Maps length bytes from the file specified by the file handle fileno,\n\
996 and returns a mmap object. If length is larger than the current size\n\
997 of the file, the file is extended to contain length bytes. If length\n\
998 is 0, the maximum length of the map is the current size of the file,\n\
999 except that if the file is empty Windows raises an exception (you cannot\n\
1000 create an empty mapping on Windows).\n\
1002 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1004 Maps length bytes from the file specified by the file descriptor fileno,\n\
1005 and returns a mmap object. If length is 0, the maximum length of the map\n\
1006 will be the current size of the file when mmap is called.\n\
1007 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1008 private copy-on-write mapping, so changes to the contents of the mmap\n\
1009 object will be private to this process, and MAP_SHARED creates a mapping\n\
1010 that's shared with all other processes mapping the same areas of the file.\n\
1011 The default value is MAP_SHARED.\n\
1013 To map anonymous memory, pass -1 as the fileno (both versions).");
1016 static PyTypeObject mmap_object_type
= {
1017 PyVarObject_HEAD_INIT(NULL
, 0)
1018 "mmap.mmap", /* tp_name */
1019 sizeof(mmap_object
), /* tp_size */
1020 0, /* tp_itemsize */
1022 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
1028 0, /* tp_as_number */
1029 &mmap_as_sequence
, /*tp_as_sequence*/
1030 &mmap_as_mapping
, /*tp_as_mapping*/
1034 PyObject_GenericGetAttr
, /*tp_getattro*/
1036 &mmap_as_buffer
, /*tp_as_buffer*/
1037 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
1038 mmap_doc
, /*tp_doc*/
1039 0, /* tp_traverse */
1041 0, /* tp_richcompare */
1042 0, /* tp_weaklistoffset */
1044 0, /* tp_iternext */
1045 mmap_object_methods
, /* tp_methods */
1050 0, /* tp_descr_get */
1051 0, /* tp_descr_set */
1052 0, /* tp_dictoffset */
1054 PyType_GenericAlloc
, /* tp_alloc */
1055 new_mmap_object
, /* tp_new */
1056 PyObject_Del
, /* tp_free */
1060 /* extract the map size from the given PyObject
1062 Returns -1 on error, with an appropriate Python exception raised. On
1063 success, the map size is returned. */
1065 _GetMapSize(PyObject
*o
, const char* param
)
1069 if (PyIndex_Check(o
)) {
1070 Py_ssize_t i
= PyNumber_AsSsize_t(o
, PyExc_OverflowError
);
1071 if (i
==-1 && PyErr_Occurred())
1074 PyErr_Format(PyExc_OverflowError
,
1075 "memory mapped %s must be positive",
1082 PyErr_SetString(PyExc_TypeError
, "map size must be an integral value");
1088 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
)
1094 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1095 Py_ssize_t map_size
, offset
;
1096 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
1098 int access
= (int)ACCESS_DEFAULT
;
1099 static char *keywords
[] = {"fileno", "length",
1101 "access", "offset", NULL
};
1103 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|iiiO", keywords
,
1104 &fd
, &map_size_obj
, &flags
, &prot
,
1105 &access
, &offset_obj
))
1107 map_size
= _GetMapSize(map_size_obj
, "size");
1110 offset
= _GetMapSize(offset_obj
, "offset");
1114 if ((access
!= (int)ACCESS_DEFAULT
) &&
1115 ((flags
!= MAP_SHARED
) || (prot
!= (PROT_WRITE
| PROT_READ
))))
1116 return PyErr_Format(PyExc_ValueError
,
1117 "mmap can't specify both access and flags, prot.");
1118 switch ((access_mode
)access
) {
1125 prot
= PROT_READ
| PROT_WRITE
;
1128 flags
= MAP_PRIVATE
;
1129 prot
= PROT_READ
| PROT_WRITE
;
1131 case ACCESS_DEFAULT
:
1132 /* use the specified or default values of flags and prot */
1135 return PyErr_Format(PyExc_ValueError
,
1136 "mmap invalid access parameter.");
1139 if (prot
== PROT_READ
) {
1140 access
= ACCESS_READ
;
1145 /* on OpenVMS we must ensure that all bytes are written to the file */
1150 if (fd
!= -1 && fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
1151 if (map_size
== 0) {
1152 map_size
= st
.st_size
;
1153 } else if ((size_t)offset
+ (size_t)map_size
> st
.st_size
) {
1154 PyErr_SetString(PyExc_ValueError
,
1155 "mmap length is greater than file size");
1160 m_obj
= (mmap_object
*)type
->tp_alloc(type
, 0);
1161 if (m_obj
== NULL
) {return NULL
;}
1163 m_obj
->size
= (size_t) map_size
;
1164 m_obj
->pos
= (size_t) 0;
1165 m_obj
->offset
= offset
;
1168 /* Assume the caller wants to map anonymous memory.
1169 This is the same behaviour as Windows. mmap.mmap(-1, size)
1170 on both Windows and Unix map anonymous memory.
1172 #ifdef MAP_ANONYMOUS
1173 /* BSD way to map anonymous memory */
1174 flags
|= MAP_ANONYMOUS
;
1176 /* SVR4 method to map anonymous memory is to open /dev/zero */
1177 fd
= devzero
= open("/dev/zero", O_RDWR
);
1178 if (devzero
== -1) {
1180 PyErr_SetFromErrno(mmap_module_error
);
1185 m_obj
->fd
= dup(fd
);
1186 if (m_obj
->fd
== -1) {
1188 PyErr_SetFromErrno(mmap_module_error
);
1193 m_obj
->data
= mmap(NULL
, map_size
,
1197 if (devzero
!= -1) {
1201 if (m_obj
->data
== (char *)-1) {
1204 PyErr_SetFromErrno(mmap_module_error
);
1207 m_obj
->access
= (access_mode
)access
;
1208 return (PyObject
*)m_obj
;
1214 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
)
1217 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1218 Py_ssize_t map_size
, offset
;
1219 DWORD off_hi
; /* upper 32 bits of offset */
1220 DWORD off_lo
; /* lower 32 bits of offset */
1221 DWORD size_hi
; /* upper 32 bits of size */
1222 DWORD size_lo
; /* lower 32 bits of size */
1227 int access
= (access_mode
)ACCESS_DEFAULT
;
1228 DWORD flProtect
, dwDesiredAccess
;
1229 static char *keywords
[] = { "fileno", "length",
1231 "access", "offset", NULL
};
1233 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|ziO", keywords
,
1234 &fileno
, &map_size_obj
,
1235 &tagname
, &access
, &offset_obj
)) {
1239 switch((access_mode
)access
) {
1241 flProtect
= PAGE_READONLY
;
1242 dwDesiredAccess
= FILE_MAP_READ
;
1244 case ACCESS_DEFAULT
: case ACCESS_WRITE
:
1245 flProtect
= PAGE_READWRITE
;
1246 dwDesiredAccess
= FILE_MAP_WRITE
;
1249 flProtect
= PAGE_WRITECOPY
;
1250 dwDesiredAccess
= FILE_MAP_COPY
;
1253 return PyErr_Format(PyExc_ValueError
,
1254 "mmap invalid access parameter.");
1257 map_size
= _GetMapSize(map_size_obj
, "size");
1260 offset
= _GetMapSize(offset_obj
, "offset");
1264 /* assume -1 and 0 both mean invalid filedescriptor
1265 to 'anonymously' map memory.
1266 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1267 XXX: Should this code be added?
1269 PyErr_Warn(PyExc_DeprecationWarning,
1270 "don't use 0 for anonymous memory");
1272 if (fileno
!= -1 && fileno
!= 0) {
1273 fh
= (HANDLE
)_get_osfhandle(fileno
);
1274 if (fh
==(HANDLE
)-1) {
1275 PyErr_SetFromErrno(mmap_module_error
);
1278 /* Win9x appears to need us seeked to zero */
1279 lseek(fileno
, 0, SEEK_SET
);
1282 m_obj
= (mmap_object
*)type
->tp_alloc(type
, 0);
1285 /* Set every field to an invalid marker, so we can safely
1286 destruct the object in the face of failure */
1288 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
1289 m_obj
->map_handle
= NULL
;
1290 m_obj
->tagname
= NULL
;
1291 m_obj
->offset
= offset
;
1294 /* It is necessary to duplicate the handle, so the
1295 Python code can close it on us */
1296 if (!DuplicateHandle(
1297 GetCurrentProcess(), /* source process handle */
1298 fh
, /* handle to be duplicated */
1299 GetCurrentProcess(), /* target proc handle */
1300 (LPHANDLE
)&m_obj
->file_handle
, /* result */
1301 0, /* access - ignored due to options value */
1302 FALSE
, /* inherited by child processes? */
1303 DUPLICATE_SAME_ACCESS
)) { /* options */
1304 dwErr
= GetLastError();
1306 PyErr_SetFromWindowsErr(dwErr
);
1311 low
= GetFileSize(fh
, &high
);
1312 /* low might just happen to have the value INVALID_FILE_SIZE;
1313 so we need to check the last error also. */
1314 if (low
== INVALID_FILE_SIZE
&&
1315 (dwErr
= GetLastError()) != NO_ERROR
) {
1317 return PyErr_SetFromWindowsErr(dwErr
);
1320 #if SIZEOF_SIZE_T > 4
1321 m_obj
->size
= (((size_t)high
)<<32) + low
;
1324 /* File is too large to map completely */
1325 m_obj
->size
= (size_t)-1;
1330 m_obj
->size
= map_size
;
1334 m_obj
->size
= map_size
;
1337 /* set the initial position */
1338 m_obj
->pos
= (size_t) 0;
1340 /* set the tag name */
1341 if (tagname
!= NULL
&& *tagname
!= '\0') {
1342 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
1343 if (m_obj
->tagname
== NULL
) {
1348 strcpy(m_obj
->tagname
, tagname
);
1351 m_obj
->tagname
= NULL
;
1353 m_obj
->access
= (access_mode
)access
;
1354 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1355 * more than 4 bytes, we need to break it apart. Else (size_t
1356 * consumes 4 bytes), C doesn't define what happens if we shift
1357 * right by 32, so we need different code.
1359 #if SIZEOF_SIZE_T > 4
1360 size_hi
= (DWORD
)((offset
+ m_obj
->size
) >> 32);
1361 size_lo
= (DWORD
)((offset
+ m_obj
->size
) & 0xFFFFFFFF);
1362 off_hi
= (DWORD
)(offset
>> 32);
1363 off_lo
= (DWORD
)(offset
& 0xFFFFFFFF);
1366 size_lo
= (DWORD
)(offset
+ m_obj
->size
);
1368 off_lo
= (DWORD
)offset
;
1370 /* For files, it would be sufficient to pass 0 as size.
1371 For anonymous maps, we have to pass the size explicitly. */
1372 m_obj
->map_handle
= CreateFileMapping(m_obj
->file_handle
,
1378 if (m_obj
->map_handle
!= NULL
) {
1379 m_obj
->data
= (char *) MapViewOfFile(m_obj
->map_handle
,
1384 if (m_obj
->data
!= NULL
)
1385 return (PyObject
*)m_obj
;
1387 dwErr
= GetLastError();
1388 CloseHandle(m_obj
->map_handle
);
1389 m_obj
->map_handle
= NULL
;
1392 dwErr
= GetLastError();
1394 PyErr_SetFromWindowsErr(dwErr
);
1397 #endif /* MS_WINDOWS */
1400 setint(PyObject
*d
, const char *name
, long value
)
1402 PyObject
*o
= PyInt_FromLong(value
);
1403 if (o
&& PyDict_SetItemString(d
, name
, o
) == 0) {
1411 PyObject
*dict
, *module
;
1413 if (PyType_Ready(&mmap_object_type
) < 0)
1416 module
= Py_InitModule("mmap", NULL
);
1419 dict
= PyModule_GetDict(module
);
1422 mmap_module_error
= PyErr_NewException("mmap.error",
1423 PyExc_EnvironmentError
, NULL
);
1424 if (mmap_module_error
== NULL
)
1426 PyDict_SetItemString(dict
, "error", mmap_module_error
);
1427 PyDict_SetItemString(dict
, "mmap", (PyObject
*) &mmap_object_type
);
1429 setint(dict
, "PROT_EXEC", PROT_EXEC
);
1432 setint(dict
, "PROT_READ", PROT_READ
);
1435 setint(dict
, "PROT_WRITE", PROT_WRITE
);
1439 setint(dict
, "MAP_SHARED", MAP_SHARED
);
1442 setint(dict
, "MAP_PRIVATE", MAP_PRIVATE
);
1444 #ifdef MAP_DENYWRITE
1445 setint(dict
, "MAP_DENYWRITE", MAP_DENYWRITE
);
1447 #ifdef MAP_EXECUTABLE
1448 setint(dict
, "MAP_EXECUTABLE", MAP_EXECUTABLE
);
1450 #ifdef MAP_ANONYMOUS
1451 setint(dict
, "MAP_ANON", MAP_ANONYMOUS
);
1452 setint(dict
, "MAP_ANONYMOUS", MAP_ANONYMOUS
);
1455 setint(dict
, "PAGESIZE", (long)my_getpagesize());
1457 setint(dict
, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1459 setint(dict
, "ACCESS_READ", ACCESS_READ
);
1460 setint(dict
, "ACCESS_WRITE", ACCESS_WRITE
);
1461 setint(dict
, "ACCESS_COPY", ACCESS_COPY
);