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
, n
;
239 if (!PyArg_ParseTuple(args
, "n:read", &num_bytes
))
242 /* silently 'adjust' out-of-range requests */
243 assert(self
->size
>= self
->pos
);
244 n
= self
->size
- self
->pos
;
245 /* The difference can overflow, only if self->size is greater than
246 * PY_SSIZE_T_MAX. But then the operation cannot possibly succeed,
247 * because the mapped area and the returned string each need more
248 * than half of the addressable memory. So we clip the size, and let
249 * the code below raise MemoryError.
253 if (num_bytes
< 0 || num_bytes
> n
) {
256 result
= Py_BuildValue("s#", self
->data
+self
->pos
, num_bytes
);
257 self
->pos
+= num_bytes
;
262 mmap_gfind(mmap_object
*self
,
266 Py_ssize_t start
= self
->pos
;
267 Py_ssize_t end
= self
->size
;
272 if (!PyArg_ParseTuple(args
, reverse
? "s#|nn:rfind" : "s#|nn:find",
273 &needle
, &len
, &start
, &end
)) {
276 const char *p
, *start_p
, *end_p
;
277 int sign
= reverse
? -1 : 1;
283 else if ((size_t)start
> self
->size
)
290 else if ((size_t)end
> self
->size
)
293 start_p
= self
->data
+ start
;
294 end_p
= self
->data
+ end
;
296 for (p
= (reverse
? end_p
- len
: start_p
);
297 (p
>= start_p
) && (p
+ len
<= end_p
); p
+= sign
) {
299 for (i
= 0; i
< len
&& needle
[i
] == p
[i
]; ++i
)
302 return PyInt_FromSsize_t(p
- self
->data
);
305 return PyInt_FromLong(-1);
310 mmap_find_method(mmap_object
*self
,
313 return mmap_gfind(self
, args
, 0);
317 mmap_rfind_method(mmap_object
*self
,
320 return mmap_gfind(self
, args
, 1);
324 is_writeable(mmap_object
*self
)
326 if (self
->access
!= ACCESS_READ
)
328 PyErr_Format(PyExc_TypeError
, "mmap can't modify a readonly memory map.");
333 is_resizeable(mmap_object
*self
)
335 if ((self
->access
== ACCESS_WRITE
) || (self
->access
== ACCESS_DEFAULT
))
337 PyErr_Format(PyExc_TypeError
,
338 "mmap can't resize a readonly or copy-on-write memory map.");
344 mmap_write_method(mmap_object
*self
,
351 if (!PyArg_ParseTuple(args
, "s#:write", &data
, &length
))
354 if (!is_writeable(self
))
357 if ((self
->pos
+ length
) > self
->size
) {
358 PyErr_SetString(PyExc_ValueError
, "data out of range");
361 memcpy(self
->data
+self
->pos
, data
, length
);
362 self
->pos
= self
->pos
+length
;
368 mmap_write_byte_method(mmap_object
*self
,
374 if (!PyArg_ParseTuple(args
, "c:write_byte", &value
))
377 if (!is_writeable(self
))
380 if (self
->pos
< self
->size
) {
381 *(self
->data
+self
->pos
) = value
;
387 PyErr_SetString(PyExc_ValueError
, "write byte out of range");
393 mmap_size_method(mmap_object
*self
,
399 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
402 low
= GetFileSize(self
->file_handle
, &high
);
403 if (low
== INVALID_FILE_SIZE
) {
404 /* It might be that the function appears to have failed,
405 when indeed its size equals INVALID_FILE_SIZE */
406 DWORD error
= GetLastError();
407 if (error
!= NO_ERROR
)
408 return PyErr_SetFromWindowsErr(error
);
410 if (!high
&& low
< LONG_MAX
)
411 return PyInt_FromLong((long)low
);
412 size
= (((PY_LONG_LONG
)high
)<<32) + low
;
413 return PyLong_FromLongLong(size
);
415 return PyInt_FromSsize_t(self
->size
);
417 #endif /* MS_WINDOWS */
422 if (-1 == fstat(self
->fd
, &buf
)) {
423 PyErr_SetFromErrno(mmap_module_error
);
426 return PyInt_FromSsize_t(buf
.st_size
);
431 /* This assumes that you want the entire file mapped,
432 / and when recreating the map will make the new file
435 / Is this really necessary? This could easily be done
436 / from python by just closing and re-opening with the
441 mmap_resize_method(mmap_object
*self
,
446 if (!PyArg_ParseTuple(args
, "n:resize", &new_size
) ||
447 !is_resizeable(self
)) {
452 DWORD off_hi
, off_lo
, newSizeLow
, newSizeHigh
;
453 /* First, unmap the file view */
454 UnmapViewOfFile(self
->data
);
456 /* Close the mapping object */
457 CloseHandle(self
->map_handle
);
458 self
->map_handle
= NULL
;
459 /* Move to the desired EOF position */
460 #if SIZEOF_SIZE_T > 4
461 newSizeHigh
= (DWORD
)((self
->offset
+ new_size
) >> 32);
462 newSizeLow
= (DWORD
)((self
->offset
+ new_size
) & 0xFFFFFFFF);
463 off_hi
= (DWORD
)(self
->offset
>> 32);
464 off_lo
= (DWORD
)(self
->offset
& 0xFFFFFFFF);
467 newSizeLow
= (DWORD
)(self
->offset
+ new_size
);
469 off_lo
= (DWORD
)self
->offset
;
471 SetFilePointer(self
->file_handle
,
472 newSizeLow
, &newSizeHigh
, FILE_BEGIN
);
473 /* Change the size of the file */
474 SetEndOfFile(self
->file_handle
);
475 /* Create another mapping object and remap the file view */
476 self
->map_handle
= CreateFileMapping(
483 if (self
->map_handle
!= NULL
) {
484 self
->data
= (char *) MapViewOfFile(self
->map_handle
,
489 if (self
->data
!= NULL
) {
490 self
->size
= new_size
;
494 dwErrCode
= GetLastError();
495 CloseHandle(self
->map_handle
);
496 self
->map_handle
= NULL
;
499 dwErrCode
= GetLastError();
501 PyErr_SetFromWindowsErr(dwErrCode
);
503 #endif /* MS_WINDOWS */
508 PyErr_SetString(PyExc_SystemError
,
509 "mmap: resizing not available--no mremap()");
515 if (ftruncate(self
->fd
, self
->offset
+ new_size
) == -1) {
516 PyErr_SetFromErrno(mmap_module_error
);
520 #ifdef MREMAP_MAYMOVE
521 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
523 #if defined(__NetBSD__)
524 newmap
= mremap(self
->data
, self
->size
, self
->data
, new_size
, 0);
526 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
527 #endif /* __NetBSD__ */
529 if (newmap
== (void *)-1)
531 PyErr_SetFromErrno(mmap_module_error
);
535 self
->size
= new_size
;
538 #endif /* HAVE_MREMAP */
544 mmap_tell_method(mmap_object
*self
, PyObject
*unused
)
547 return PyInt_FromSize_t(self
->pos
);
551 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
553 Py_ssize_t offset
= 0;
554 Py_ssize_t size
= self
->size
;
556 if (!PyArg_ParseTuple(args
, "|nn:flush", &offset
, &size
))
558 if ((size_t)(offset
+ size
) > self
->size
) {
559 PyErr_SetString(PyExc_ValueError
, "flush values out of range");
563 return PyInt_FromLong((long) FlushViewOfFile(self
->data
+offset
, size
));
565 /* XXX semantics of return value? */
566 /* XXX flags for msync? */
567 if (-1 == msync(self
->data
+ offset
, size
, MS_SYNC
)) {
568 PyErr_SetFromErrno(mmap_module_error
);
571 return PyInt_FromLong(0);
573 PyErr_SetString(PyExc_ValueError
, "flush not supported on this system");
579 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
584 if (!PyArg_ParseTuple(args
, "n|i:seek", &dist
, &how
))
589 case 0: /* relative to start */
594 case 1: /* relative to current position */
595 if ((Py_ssize_t
)self
->pos
+ dist
< 0)
597 where
= self
->pos
+ dist
;
599 case 2: /* relative to end */
600 if ((Py_ssize_t
)self
->size
+ dist
< 0)
602 where
= self
->size
+ dist
;
605 PyErr_SetString(PyExc_ValueError
, "unknown seek type");
608 if (where
> self
->size
)
616 PyErr_SetString(PyExc_ValueError
, "seek out of range");
621 mmap_move_method(mmap_object
*self
, PyObject
*args
)
623 unsigned long dest
, src
, cnt
;
625 if (!PyArg_ParseTuple(args
, "kkk:move", &dest
, &src
, &cnt
) ||
626 !is_writeable(self
)) {
629 /* bounds check the values */
630 if (cnt
< 0 || (cnt
+ dest
) < cnt
|| (cnt
+ src
) < cnt
||
631 src
< 0 || src
> self
->size
|| (src
+ cnt
) > self
->size
||
632 dest
< 0 || dest
> self
->size
|| (dest
+ cnt
) > self
->size
) {
633 PyErr_SetString(PyExc_ValueError
,
634 "source, destination, or count out of range");
637 memmove(self
->data
+dest
, self
->data
+src
, cnt
);
643 static struct PyMethodDef mmap_object_methods
[] = {
644 {"close", (PyCFunction
) mmap_close_method
, METH_NOARGS
},
645 {"find", (PyCFunction
) mmap_find_method
, METH_VARARGS
},
646 {"rfind", (PyCFunction
) mmap_rfind_method
, METH_VARARGS
},
647 {"flush", (PyCFunction
) mmap_flush_method
, METH_VARARGS
},
648 {"move", (PyCFunction
) mmap_move_method
, METH_VARARGS
},
649 {"read", (PyCFunction
) mmap_read_method
, METH_VARARGS
},
650 {"read_byte", (PyCFunction
) mmap_read_byte_method
, METH_NOARGS
},
651 {"readline", (PyCFunction
) mmap_read_line_method
, METH_NOARGS
},
652 {"resize", (PyCFunction
) mmap_resize_method
, METH_VARARGS
},
653 {"seek", (PyCFunction
) mmap_seek_method
, METH_VARARGS
},
654 {"size", (PyCFunction
) mmap_size_method
, METH_NOARGS
},
655 {"tell", (PyCFunction
) mmap_tell_method
, METH_NOARGS
},
656 {"write", (PyCFunction
) mmap_write_method
, METH_VARARGS
},
657 {"write_byte", (PyCFunction
) mmap_write_byte_method
, METH_VARARGS
},
658 {NULL
, NULL
} /* sentinel */
661 /* Functions for treating an mmap'ed file as a buffer */
664 mmap_buffer_getreadbuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
668 PyErr_SetString(PyExc_SystemError
,
669 "Accessing non-existent mmap segment");
677 mmap_buffer_getwritebuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
681 PyErr_SetString(PyExc_SystemError
,
682 "Accessing non-existent mmap segment");
685 if (!is_writeable(self
))
692 mmap_buffer_getsegcount(mmap_object
*self
, Py_ssize_t
*lenp
)
701 mmap_buffer_getcharbuffer(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
704 PyErr_SetString(PyExc_SystemError
,
705 "accessing non-existent buffer segment");
708 *ptr
= (const char *)self
->data
;
713 mmap_length(mmap_object
*self
)
720 mmap_item(mmap_object
*self
, Py_ssize_t i
)
723 if (i
< 0 || (size_t)i
>= self
->size
) {
724 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
727 return PyString_FromStringAndSize(self
->data
+ i
, 1);
731 mmap_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
)
736 else if ((size_t)ilow
> self
->size
)
742 else if ((size_t)ihigh
> self
->size
)
745 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
749 mmap_subscript(mmap_object
*self
, PyObject
*item
)
752 if (PyIndex_Check(item
)) {
753 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
754 if (i
== -1 && PyErr_Occurred())
758 if (i
< 0 || (size_t)i
>= self
->size
) {
759 PyErr_SetString(PyExc_IndexError
,
760 "mmap index out of range");
763 return PyString_FromStringAndSize(self
->data
+ i
, 1);
765 else if (PySlice_Check(item
)) {
766 Py_ssize_t start
, stop
, step
, slicelen
;
768 if (PySlice_GetIndicesEx((PySliceObject
*)item
, self
->size
,
769 &start
, &stop
, &step
, &slicelen
) < 0) {
774 return PyString_FromStringAndSize("", 0);
776 return PyString_FromStringAndSize(self
->data
+ start
,
779 char *result_buf
= (char *)PyMem_Malloc(slicelen
);
783 if (result_buf
== NULL
)
784 return PyErr_NoMemory();
785 for (cur
= start
, i
= 0; i
< slicelen
;
787 result_buf
[i
] = self
->data
[cur
];
789 result
= PyString_FromStringAndSize(result_buf
,
791 PyMem_Free(result_buf
);
796 PyErr_SetString(PyExc_TypeError
,
797 "mmap indices must be integers");
803 mmap_concat(mmap_object
*self
, PyObject
*bb
)
806 PyErr_SetString(PyExc_SystemError
,
807 "mmaps don't support concatenation");
812 mmap_repeat(mmap_object
*self
, Py_ssize_t n
)
815 PyErr_SetString(PyExc_SystemError
,
816 "mmaps don't support repeat operation");
821 mmap_ass_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
, PyObject
*v
)
828 else if ((size_t)ilow
> self
->size
)
834 else if ((size_t)ihigh
> self
->size
)
838 PyErr_SetString(PyExc_TypeError
,
839 "mmap object doesn't support slice deletion");
842 if (! (PyString_Check(v
)) ) {
843 PyErr_SetString(PyExc_IndexError
,
844 "mmap slice assignment must be a string");
847 if (PyString_Size(v
) != (ihigh
- ilow
)) {
848 PyErr_SetString(PyExc_IndexError
,
849 "mmap slice assignment is wrong size");
852 if (!is_writeable(self
))
854 buf
= PyString_AsString(v
);
855 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
860 mmap_ass_item(mmap_object
*self
, Py_ssize_t i
, PyObject
*v
)
865 if (i
< 0 || (size_t)i
>= self
->size
) {
866 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
870 PyErr_SetString(PyExc_TypeError
,
871 "mmap object doesn't support item deletion");
874 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
875 PyErr_SetString(PyExc_IndexError
,
876 "mmap assignment must be single-character string");
879 if (!is_writeable(self
))
881 buf
= PyString_AsString(v
);
882 self
->data
[i
] = buf
[0];
887 mmap_ass_subscript(mmap_object
*self
, PyObject
*item
, PyObject
*value
)
891 if (PyIndex_Check(item
)) {
892 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
895 if (i
== -1 && PyErr_Occurred())
899 if (i
< 0 || (size_t)i
>= self
->size
) {
900 PyErr_SetString(PyExc_IndexError
,
901 "mmap index out of range");
905 PyErr_SetString(PyExc_TypeError
,
906 "mmap object doesn't support item deletion");
909 if (!PyString_Check(value
) || PyString_Size(value
) != 1) {
910 PyErr_SetString(PyExc_IndexError
,
911 "mmap assignment must be single-character string");
914 if (!is_writeable(self
))
916 buf
= PyString_AsString(value
);
917 self
->data
[i
] = buf
[0];
920 else if (PySlice_Check(item
)) {
921 Py_ssize_t start
, stop
, step
, slicelen
;
923 if (PySlice_GetIndicesEx((PySliceObject
*)item
,
924 self
->size
, &start
, &stop
,
925 &step
, &slicelen
) < 0) {
929 PyErr_SetString(PyExc_TypeError
,
930 "mmap object doesn't support slice deletion");
933 if (!PyString_Check(value
)) {
934 PyErr_SetString(PyExc_IndexError
,
935 "mmap slice assignment must be a string");
938 if (PyString_Size(value
) != slicelen
) {
939 PyErr_SetString(PyExc_IndexError
,
940 "mmap slice assignment is wrong size");
943 if (!is_writeable(self
))
948 else if (step
== 1) {
949 const char *buf
= PyString_AsString(value
);
953 memcpy(self
->data
+ start
, buf
, slicelen
);
958 const char *buf
= PyString_AsString(value
);
962 for (cur
= start
, i
= 0; i
< slicelen
;
964 self
->data
[cur
] = buf
[i
];
970 PyErr_SetString(PyExc_TypeError
,
971 "mmap indices must be integer");
976 static PySequenceMethods mmap_as_sequence
= {
977 (lenfunc
)mmap_length
, /*sq_length*/
978 (binaryfunc
)mmap_concat
, /*sq_concat*/
979 (ssizeargfunc
)mmap_repeat
, /*sq_repeat*/
980 (ssizeargfunc
)mmap_item
, /*sq_item*/
981 (ssizessizeargfunc
)mmap_slice
, /*sq_slice*/
982 (ssizeobjargproc
)mmap_ass_item
, /*sq_ass_item*/
983 (ssizessizeobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
986 static PyMappingMethods mmap_as_mapping
= {
987 (lenfunc
)mmap_length
,
988 (binaryfunc
)mmap_subscript
,
989 (objobjargproc
)mmap_ass_subscript
,
992 static PyBufferProcs mmap_as_buffer
= {
993 (readbufferproc
)mmap_buffer_getreadbuf
,
994 (writebufferproc
)mmap_buffer_getwritebuf
,
995 (segcountproc
)mmap_buffer_getsegcount
,
996 (charbufferproc
)mmap_buffer_getcharbuffer
,
1000 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
);
1002 PyDoc_STRVAR(mmap_doc
,
1003 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
1005 Maps length bytes from the file specified by the file handle fileno,\n\
1006 and returns a mmap object. If length is larger than the current size\n\
1007 of the file, the file is extended to contain length bytes. If length\n\
1008 is 0, the maximum length of the map is the current size of the file,\n\
1009 except that if the file is empty Windows raises an exception (you cannot\n\
1010 create an empty mapping on Windows).\n\
1012 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1014 Maps length bytes from the file specified by the file descriptor fileno,\n\
1015 and returns a mmap object. If length is 0, the maximum length of the map\n\
1016 will be the current size of the file when mmap is called.\n\
1017 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1018 private copy-on-write mapping, so changes to the contents of the mmap\n\
1019 object will be private to this process, and MAP_SHARED creates a mapping\n\
1020 that's shared with all other processes mapping the same areas of the file.\n\
1021 The default value is MAP_SHARED.\n\
1023 To map anonymous memory, pass -1 as the fileno (both versions).");
1026 static PyTypeObject mmap_object_type
= {
1027 PyVarObject_HEAD_INIT(NULL
, 0)
1028 "mmap.mmap", /* tp_name */
1029 sizeof(mmap_object
), /* tp_size */
1030 0, /* tp_itemsize */
1032 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
1038 0, /* tp_as_number */
1039 &mmap_as_sequence
, /*tp_as_sequence*/
1040 &mmap_as_mapping
, /*tp_as_mapping*/
1044 PyObject_GenericGetAttr
, /*tp_getattro*/
1046 &mmap_as_buffer
, /*tp_as_buffer*/
1047 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
1048 mmap_doc
, /*tp_doc*/
1049 0, /* tp_traverse */
1051 0, /* tp_richcompare */
1052 0, /* tp_weaklistoffset */
1054 0, /* tp_iternext */
1055 mmap_object_methods
, /* tp_methods */
1060 0, /* tp_descr_get */
1061 0, /* tp_descr_set */
1062 0, /* tp_dictoffset */
1064 PyType_GenericAlloc
, /* tp_alloc */
1065 new_mmap_object
, /* tp_new */
1066 PyObject_Del
, /* tp_free */
1070 /* extract the map size from the given PyObject
1072 Returns -1 on error, with an appropriate Python exception raised. On
1073 success, the map size is returned. */
1075 _GetMapSize(PyObject
*o
, const char* param
)
1079 if (PyIndex_Check(o
)) {
1080 Py_ssize_t i
= PyNumber_AsSsize_t(o
, PyExc_OverflowError
);
1081 if (i
==-1 && PyErr_Occurred())
1084 PyErr_Format(PyExc_OverflowError
,
1085 "memory mapped %s must be positive",
1092 PyErr_SetString(PyExc_TypeError
, "map size must be an integral value");
1098 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
)
1104 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1105 Py_ssize_t map_size
, offset
;
1106 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
1108 int access
= (int)ACCESS_DEFAULT
;
1109 static char *keywords
[] = {"fileno", "length",
1111 "access", "offset", NULL
};
1113 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|iiiO", keywords
,
1114 &fd
, &map_size_obj
, &flags
, &prot
,
1115 &access
, &offset_obj
))
1117 map_size
= _GetMapSize(map_size_obj
, "size");
1120 offset
= _GetMapSize(offset_obj
, "offset");
1124 if ((access
!= (int)ACCESS_DEFAULT
) &&
1125 ((flags
!= MAP_SHARED
) || (prot
!= (PROT_WRITE
| PROT_READ
))))
1126 return PyErr_Format(PyExc_ValueError
,
1127 "mmap can't specify both access and flags, prot.");
1128 switch ((access_mode
)access
) {
1135 prot
= PROT_READ
| PROT_WRITE
;
1138 flags
= MAP_PRIVATE
;
1139 prot
= PROT_READ
| PROT_WRITE
;
1141 case ACCESS_DEFAULT
:
1142 /* use the specified or default values of flags and prot */
1145 return PyErr_Format(PyExc_ValueError
,
1146 "mmap invalid access parameter.");
1149 if (prot
== PROT_READ
) {
1150 access
= ACCESS_READ
;
1155 /* on OpenVMS we must ensure that all bytes are written to the file */
1160 if (fd
!= -1 && fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
1161 if (map_size
== 0) {
1162 map_size
= st
.st_size
;
1163 } else if ((size_t)offset
+ (size_t)map_size
> st
.st_size
) {
1164 PyErr_SetString(PyExc_ValueError
,
1165 "mmap length is greater than file size");
1170 m_obj
= (mmap_object
*)type
->tp_alloc(type
, 0);
1171 if (m_obj
== NULL
) {return NULL
;}
1173 m_obj
->size
= (size_t) map_size
;
1174 m_obj
->pos
= (size_t) 0;
1175 m_obj
->offset
= offset
;
1178 /* Assume the caller wants to map anonymous memory.
1179 This is the same behaviour as Windows. mmap.mmap(-1, size)
1180 on both Windows and Unix map anonymous memory.
1182 #ifdef MAP_ANONYMOUS
1183 /* BSD way to map anonymous memory */
1184 flags
|= MAP_ANONYMOUS
;
1186 /* SVR4 method to map anonymous memory is to open /dev/zero */
1187 fd
= devzero
= open("/dev/zero", O_RDWR
);
1188 if (devzero
== -1) {
1190 PyErr_SetFromErrno(mmap_module_error
);
1195 m_obj
->fd
= dup(fd
);
1196 if (m_obj
->fd
== -1) {
1198 PyErr_SetFromErrno(mmap_module_error
);
1203 m_obj
->data
= mmap(NULL
, map_size
,
1207 if (devzero
!= -1) {
1211 if (m_obj
->data
== (char *)-1) {
1214 PyErr_SetFromErrno(mmap_module_error
);
1217 m_obj
->access
= (access_mode
)access
;
1218 return (PyObject
*)m_obj
;
1224 new_mmap_object(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwdict
)
1227 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1228 Py_ssize_t map_size
, offset
;
1229 DWORD off_hi
; /* upper 32 bits of offset */
1230 DWORD off_lo
; /* lower 32 bits of offset */
1231 DWORD size_hi
; /* upper 32 bits of size */
1232 DWORD size_lo
; /* lower 32 bits of size */
1237 int access
= (access_mode
)ACCESS_DEFAULT
;
1238 DWORD flProtect
, dwDesiredAccess
;
1239 static char *keywords
[] = { "fileno", "length",
1241 "access", "offset", NULL
};
1243 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|ziO", keywords
,
1244 &fileno
, &map_size_obj
,
1245 &tagname
, &access
, &offset_obj
)) {
1249 switch((access_mode
)access
) {
1251 flProtect
= PAGE_READONLY
;
1252 dwDesiredAccess
= FILE_MAP_READ
;
1254 case ACCESS_DEFAULT
: case ACCESS_WRITE
:
1255 flProtect
= PAGE_READWRITE
;
1256 dwDesiredAccess
= FILE_MAP_WRITE
;
1259 flProtect
= PAGE_WRITECOPY
;
1260 dwDesiredAccess
= FILE_MAP_COPY
;
1263 return PyErr_Format(PyExc_ValueError
,
1264 "mmap invalid access parameter.");
1267 map_size
= _GetMapSize(map_size_obj
, "size");
1270 offset
= _GetMapSize(offset_obj
, "offset");
1274 /* assume -1 and 0 both mean invalid filedescriptor
1275 to 'anonymously' map memory.
1276 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1277 XXX: Should this code be added?
1279 PyErr_Warn(PyExc_DeprecationWarning,
1280 "don't use 0 for anonymous memory");
1282 if (fileno
!= -1 && fileno
!= 0) {
1283 fh
= (HANDLE
)_get_osfhandle(fileno
);
1284 if (fh
==(HANDLE
)-1) {
1285 PyErr_SetFromErrno(mmap_module_error
);
1288 /* Win9x appears to need us seeked to zero */
1289 lseek(fileno
, 0, SEEK_SET
);
1292 m_obj
= (mmap_object
*)type
->tp_alloc(type
, 0);
1295 /* Set every field to an invalid marker, so we can safely
1296 destruct the object in the face of failure */
1298 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
1299 m_obj
->map_handle
= NULL
;
1300 m_obj
->tagname
= NULL
;
1301 m_obj
->offset
= offset
;
1304 /* It is necessary to duplicate the handle, so the
1305 Python code can close it on us */
1306 if (!DuplicateHandle(
1307 GetCurrentProcess(), /* source process handle */
1308 fh
, /* handle to be duplicated */
1309 GetCurrentProcess(), /* target proc handle */
1310 (LPHANDLE
)&m_obj
->file_handle
, /* result */
1311 0, /* access - ignored due to options value */
1312 FALSE
, /* inherited by child processes? */
1313 DUPLICATE_SAME_ACCESS
)) { /* options */
1314 dwErr
= GetLastError();
1316 PyErr_SetFromWindowsErr(dwErr
);
1321 low
= GetFileSize(fh
, &high
);
1322 /* low might just happen to have the value INVALID_FILE_SIZE;
1323 so we need to check the last error also. */
1324 if (low
== INVALID_FILE_SIZE
&&
1325 (dwErr
= GetLastError()) != NO_ERROR
) {
1327 return PyErr_SetFromWindowsErr(dwErr
);
1330 #if SIZEOF_SIZE_T > 4
1331 m_obj
->size
= (((size_t)high
)<<32) + low
;
1334 /* File is too large to map completely */
1335 m_obj
->size
= (size_t)-1;
1340 m_obj
->size
= map_size
;
1344 m_obj
->size
= map_size
;
1347 /* set the initial position */
1348 m_obj
->pos
= (size_t) 0;
1350 /* set the tag name */
1351 if (tagname
!= NULL
&& *tagname
!= '\0') {
1352 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
1353 if (m_obj
->tagname
== NULL
) {
1358 strcpy(m_obj
->tagname
, tagname
);
1361 m_obj
->tagname
= NULL
;
1363 m_obj
->access
= (access_mode
)access
;
1364 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1365 * more than 4 bytes, we need to break it apart. Else (size_t
1366 * consumes 4 bytes), C doesn't define what happens if we shift
1367 * right by 32, so we need different code.
1369 #if SIZEOF_SIZE_T > 4
1370 size_hi
= (DWORD
)((offset
+ m_obj
->size
) >> 32);
1371 size_lo
= (DWORD
)((offset
+ m_obj
->size
) & 0xFFFFFFFF);
1372 off_hi
= (DWORD
)(offset
>> 32);
1373 off_lo
= (DWORD
)(offset
& 0xFFFFFFFF);
1376 size_lo
= (DWORD
)(offset
+ m_obj
->size
);
1378 off_lo
= (DWORD
)offset
;
1380 /* For files, it would be sufficient to pass 0 as size.
1381 For anonymous maps, we have to pass the size explicitly. */
1382 m_obj
->map_handle
= CreateFileMapping(m_obj
->file_handle
,
1388 if (m_obj
->map_handle
!= NULL
) {
1389 m_obj
->data
= (char *) MapViewOfFile(m_obj
->map_handle
,
1394 if (m_obj
->data
!= NULL
)
1395 return (PyObject
*)m_obj
;
1397 dwErr
= GetLastError();
1398 CloseHandle(m_obj
->map_handle
);
1399 m_obj
->map_handle
= NULL
;
1402 dwErr
= GetLastError();
1404 PyErr_SetFromWindowsErr(dwErr
);
1407 #endif /* MS_WINDOWS */
1410 setint(PyObject
*d
, const char *name
, long value
)
1412 PyObject
*o
= PyInt_FromLong(value
);
1413 if (o
&& PyDict_SetItemString(d
, name
, o
) == 0) {
1421 PyObject
*dict
, *module
;
1423 if (PyType_Ready(&mmap_object_type
) < 0)
1426 module
= Py_InitModule("mmap", NULL
);
1429 dict
= PyModule_GetDict(module
);
1432 mmap_module_error
= PyErr_NewException("mmap.error",
1433 PyExc_EnvironmentError
, NULL
);
1434 if (mmap_module_error
== NULL
)
1436 PyDict_SetItemString(dict
, "error", mmap_module_error
);
1437 PyDict_SetItemString(dict
, "mmap", (PyObject
*) &mmap_object_type
);
1439 setint(dict
, "PROT_EXEC", PROT_EXEC
);
1442 setint(dict
, "PROT_READ", PROT_READ
);
1445 setint(dict
, "PROT_WRITE", PROT_WRITE
);
1449 setint(dict
, "MAP_SHARED", MAP_SHARED
);
1452 setint(dict
, "MAP_PRIVATE", MAP_PRIVATE
);
1454 #ifdef MAP_DENYWRITE
1455 setint(dict
, "MAP_DENYWRITE", MAP_DENYWRITE
);
1457 #ifdef MAP_EXECUTABLE
1458 setint(dict
, "MAP_EXECUTABLE", MAP_EXECUTABLE
);
1460 #ifdef MAP_ANONYMOUS
1461 setint(dict
, "MAP_ANON", MAP_ANONYMOUS
);
1462 setint(dict
, "MAP_ANONYMOUS", MAP_ANONYMOUS
);
1465 setint(dict
, "PAGESIZE", (long)my_getpagesize());
1467 setint(dict
, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1469 setint(dict
, "ACCESS_READ", ACCESS_READ
);
1470 setint(dict
, "ACCESS_WRITE", ACCESS_WRITE
);
1471 setint(dict
, "ACCESS_COPY", ACCESS_COPY
);