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
!= INVALID_HANDLE_VALUE
)
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
);
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
!= INVALID_HANDLE_VALUE
) {
151 CloseHandle(self
->map_handle
);
152 self
->map_handle
= INVALID_HANDLE_VALUE
;
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 == INVALID_HANDLE_VALUE) { \
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 ((self
->pos
+ num_bytes
) > self
->size
) {
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_find_method(mmap_object
*self
,
254 Py_ssize_t start
= self
->pos
;
259 if (!PyArg_ParseTuple(args
, "s#|n:find", &needle
, &len
, &start
)) {
263 char *e
= self
->data
+ self
->size
;
269 else if ((size_t)start
> self
->size
)
272 for (p
= self
->data
+ start
; p
+ len
<= e
; ++p
) {
274 for (i
= 0; i
< len
&& needle
[i
] == p
[i
]; ++i
)
277 return PyInt_FromSsize_t(p
- self
->data
);
280 return PyInt_FromLong(-1);
285 is_writeable(mmap_object
*self
)
287 if (self
->access
!= ACCESS_READ
)
289 PyErr_Format(PyExc_TypeError
, "mmap can't modify a readonly memory map.");
294 is_resizeable(mmap_object
*self
)
296 if ((self
->access
== ACCESS_WRITE
) || (self
->access
== ACCESS_DEFAULT
))
298 PyErr_Format(PyExc_TypeError
,
299 "mmap can't resize a readonly or copy-on-write memory map.");
305 mmap_write_method(mmap_object
*self
,
312 if (!PyArg_ParseTuple(args
, "s#:write", &data
, &length
))
315 if (!is_writeable(self
))
318 if ((self
->pos
+ length
) > self
->size
) {
319 PyErr_SetString(PyExc_ValueError
, "data out of range");
322 memcpy(self
->data
+self
->pos
, data
, length
);
323 self
->pos
= self
->pos
+length
;
329 mmap_write_byte_method(mmap_object
*self
,
335 if (!PyArg_ParseTuple(args
, "c:write_byte", &value
))
338 if (!is_writeable(self
))
340 *(self
->data
+self
->pos
) = value
;
347 mmap_size_method(mmap_object
*self
,
353 if (self
->file_handle
!= INVALID_HANDLE_VALUE
) {
356 low
= GetFileSize(self
->file_handle
, &high
);
357 if (low
== INVALID_FILE_SIZE
) {
358 /* It might be that the function appears to have failed,
359 when indeed its size equals INVALID_FILE_SIZE */
360 DWORD error
= GetLastError();
361 if (error
!= NO_ERROR
)
362 return PyErr_SetFromWindowsErr(error
);
364 if (!high
&& low
< LONG_MAX
)
365 return PyInt_FromLong((long)low
);
366 size
= (((PY_LONG_LONG
)high
)<<32) + low
;
367 return PyLong_FromLongLong(size
);
369 return PyInt_FromSsize_t(self
->size
);
371 #endif /* MS_WINDOWS */
376 if (-1 == fstat(self
->fd
, &buf
)) {
377 PyErr_SetFromErrno(mmap_module_error
);
380 return PyInt_FromSsize_t(buf
.st_size
);
385 /* This assumes that you want the entire file mapped,
386 / and when recreating the map will make the new file
389 / Is this really necessary? This could easily be done
390 / from python by just closing and re-opening with the
395 mmap_resize_method(mmap_object
*self
,
400 if (!PyArg_ParseTuple(args
, "n:resize", &new_size
) ||
401 !is_resizeable(self
)) {
406 DWORD off_hi
, off_lo
, newSizeLow
, newSizeHigh
;
407 /* First, unmap the file view */
408 UnmapViewOfFile(self
->data
);
409 /* Close the mapping object */
410 CloseHandle(self
->map_handle
);
411 /* Move to the desired EOF position */
412 #if SIZEOF_SIZE_T > 4
413 newSizeHigh
= (DWORD
)((self
->offset
+ new_size
) >> 32);
414 newSizeLow
= (DWORD
)((self
->offset
+ new_size
) & 0xFFFFFFFF);
415 off_hi
= (DWORD
)(self
->offset
>> 32);
416 off_lo
= (DWORD
)(self
->offset
& 0xFFFFFFFF);
419 newSizeLow
= (DWORD
)new_size
;
421 off_lo
= (DWORD
)self
->offset
;
423 SetFilePointer(self
->file_handle
,
424 newSizeLow
, &newSizeHigh
, FILE_BEGIN
);
425 /* Change the size of the file */
426 SetEndOfFile(self
->file_handle
);
427 /* Create another mapping object and remap the file view */
428 self
->map_handle
= CreateFileMapping(
435 if (self
->map_handle
!= NULL
) {
436 self
->data
= (char *) MapViewOfFile(self
->map_handle
,
441 if (self
->data
!= NULL
) {
442 self
->size
= new_size
;
446 dwErrCode
= GetLastError();
449 dwErrCode
= GetLastError();
451 PyErr_SetFromWindowsErr(dwErrCode
);
453 #endif /* MS_WINDOWS */
458 PyErr_SetString(PyExc_SystemError
,
459 "mmap: resizing not available--no mremap()");
465 if (ftruncate(self
->fd
, new_size
) == -1) {
466 PyErr_SetFromErrno(mmap_module_error
);
470 #ifdef MREMAP_MAYMOVE
471 newmap
= mremap(self
->data
, self
->size
, new_size
, MREMAP_MAYMOVE
);
473 newmap
= mremap(self
->data
, self
->size
, new_size
, 0);
475 if (newmap
== (void *)-1)
477 PyErr_SetFromErrno(mmap_module_error
);
481 self
->size
= new_size
;
484 #endif /* HAVE_MREMAP */
490 mmap_tell_method(mmap_object
*self
, PyObject
*unused
)
493 return PyInt_FromSize_t(self
->pos
);
497 mmap_flush_method(mmap_object
*self
, PyObject
*args
)
499 Py_ssize_t offset
= 0;
500 Py_ssize_t size
= self
->size
;
502 if (!PyArg_ParseTuple(args
, "|nn:flush", &offset
, &size
))
504 if ((size_t)(offset
+ size
) > self
->size
) {
505 PyErr_SetString(PyExc_ValueError
, "flush values out of range");
509 return PyInt_FromLong((long)
510 FlushViewOfFile(self
->data
+offset
, size
));
511 #endif /* MS_WINDOWS */
513 /* XXX semantics of return value? */
514 /* XXX flags for msync? */
515 if (-1 == msync(self
->data
+ offset
, size
,
518 PyErr_SetFromErrno(mmap_module_error
);
521 return PyInt_FromLong(0);
527 mmap_seek_method(mmap_object
*self
, PyObject
*args
)
532 if (!PyArg_ParseTuple(args
, "n|i:seek", &dist
, &how
))
537 case 0: /* relative to start */
542 case 1: /* relative to current position */
543 if ((Py_ssize_t
)self
->pos
+ dist
< 0)
545 where
= self
->pos
+ dist
;
547 case 2: /* relative to end */
548 if ((Py_ssize_t
)self
->size
+ dist
< 0)
550 where
= self
->size
+ dist
;
553 PyErr_SetString(PyExc_ValueError
, "unknown seek type");
556 if (where
> self
->size
)
564 PyErr_SetString(PyExc_ValueError
, "seek out of range");
569 mmap_move_method(mmap_object
*self
, PyObject
*args
)
571 unsigned long dest
, src
, count
;
573 if (!PyArg_ParseTuple(args
, "kkk:move", &dest
, &src
, &count
) ||
574 !is_writeable(self
)) {
577 /* bounds check the values */
578 if (/* end of source after end of data?? */
579 ((src
+count
) > self
->size
)
581 || (dest
+count
> self
->size
)) {
582 PyErr_SetString(PyExc_ValueError
,
583 "source or destination out of range");
586 memmove(self
->data
+dest
, self
->data
+src
, count
);
593 static struct PyMethodDef mmap_object_methods
[] = {
594 {"close", (PyCFunction
) mmap_close_method
, METH_NOARGS
},
595 {"find", (PyCFunction
) mmap_find_method
, METH_VARARGS
},
596 {"flush", (PyCFunction
) mmap_flush_method
, METH_VARARGS
},
597 {"move", (PyCFunction
) mmap_move_method
, METH_VARARGS
},
598 {"read", (PyCFunction
) mmap_read_method
, METH_VARARGS
},
599 {"read_byte", (PyCFunction
) mmap_read_byte_method
, METH_NOARGS
},
600 {"readline", (PyCFunction
) mmap_read_line_method
, METH_NOARGS
},
601 {"resize", (PyCFunction
) mmap_resize_method
, METH_VARARGS
},
602 {"seek", (PyCFunction
) mmap_seek_method
, METH_VARARGS
},
603 {"size", (PyCFunction
) mmap_size_method
, METH_NOARGS
},
604 {"tell", (PyCFunction
) mmap_tell_method
, METH_NOARGS
},
605 {"write", (PyCFunction
) mmap_write_method
, METH_VARARGS
},
606 {"write_byte", (PyCFunction
) mmap_write_byte_method
, METH_VARARGS
},
607 {NULL
, NULL
} /* sentinel */
610 /* Functions for treating an mmap'ed file as a buffer */
613 mmap_buffer_getreadbuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
617 PyErr_SetString(PyExc_SystemError
,
618 "Accessing non-existent mmap segment");
626 mmap_buffer_getwritebuf(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
630 PyErr_SetString(PyExc_SystemError
,
631 "Accessing non-existent mmap segment");
634 if (!is_writeable(self
))
641 mmap_buffer_getsegcount(mmap_object
*self
, Py_ssize_t
*lenp
)
650 mmap_buffer_getcharbuffer(mmap_object
*self
, Py_ssize_t index
, const void **ptr
)
653 PyErr_SetString(PyExc_SystemError
,
654 "accessing non-existent buffer segment");
657 *ptr
= (const char *)self
->data
;
662 mmap_object_getattr(mmap_object
*self
, char *name
)
664 return Py_FindMethod(mmap_object_methods
, (PyObject
*)self
, name
);
668 mmap_length(mmap_object
*self
)
675 mmap_item(mmap_object
*self
, Py_ssize_t i
)
678 if (i
< 0 || (size_t)i
>= self
->size
) {
679 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
682 return PyString_FromStringAndSize(self
->data
+ i
, 1);
686 mmap_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
)
691 else if ((size_t)ilow
> self
->size
)
697 else if ((size_t)ihigh
> self
->size
)
700 return PyString_FromStringAndSize(self
->data
+ ilow
, ihigh
-ilow
);
704 mmap_subscript(mmap_object
*self
, PyObject
*item
)
707 if (PyIndex_Check(item
)) {
708 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
709 if (i
== -1 && PyErr_Occurred())
713 if (i
< 0 || (size_t)i
> self
->size
) {
714 PyErr_SetString(PyExc_IndexError
,
715 "mmap index out of range");
718 return PyString_FromStringAndSize(self
->data
+ i
, 1);
720 else if (PySlice_Check(item
)) {
721 Py_ssize_t start
, stop
, step
, slicelen
;
723 if (PySlice_GetIndicesEx((PySliceObject
*)item
, self
->size
,
724 &start
, &stop
, &step
, &slicelen
) < 0) {
729 return PyString_FromStringAndSize("", 0);
731 return PyString_FromStringAndSize(self
->data
+ start
,
734 char *result_buf
= (char *)PyMem_Malloc(slicelen
);
738 if (result_buf
== NULL
)
739 return PyErr_NoMemory();
740 for (cur
= start
, i
= 0; i
< slicelen
;
742 result_buf
[i
] = self
->data
[cur
];
744 result
= PyString_FromStringAndSize(result_buf
,
746 PyMem_Free(result_buf
);
751 PyErr_SetString(PyExc_TypeError
,
752 "mmap indices must be integers");
758 mmap_concat(mmap_object
*self
, PyObject
*bb
)
761 PyErr_SetString(PyExc_SystemError
,
762 "mmaps don't support concatenation");
767 mmap_repeat(mmap_object
*self
, Py_ssize_t n
)
770 PyErr_SetString(PyExc_SystemError
,
771 "mmaps don't support repeat operation");
776 mmap_ass_slice(mmap_object
*self
, Py_ssize_t ilow
, Py_ssize_t ihigh
, PyObject
*v
)
783 else if ((size_t)ilow
> self
->size
)
789 else if ((size_t)ihigh
> self
->size
)
793 PyErr_SetString(PyExc_TypeError
,
794 "mmap object doesn't support slice deletion");
797 if (! (PyString_Check(v
)) ) {
798 PyErr_SetString(PyExc_IndexError
,
799 "mmap slice assignment must be a string");
802 if (PyString_Size(v
) != (ihigh
- ilow
)) {
803 PyErr_SetString(PyExc_IndexError
,
804 "mmap slice assignment is wrong size");
807 if (!is_writeable(self
))
809 buf
= PyString_AsString(v
);
810 memcpy(self
->data
+ ilow
, buf
, ihigh
-ilow
);
815 mmap_ass_item(mmap_object
*self
, Py_ssize_t i
, PyObject
*v
)
820 if (i
< 0 || (size_t)i
>= self
->size
) {
821 PyErr_SetString(PyExc_IndexError
, "mmap index out of range");
825 PyErr_SetString(PyExc_TypeError
,
826 "mmap object doesn't support item deletion");
829 if (! (PyString_Check(v
) && PyString_Size(v
)==1) ) {
830 PyErr_SetString(PyExc_IndexError
,
831 "mmap assignment must be single-character string");
834 if (!is_writeable(self
))
836 buf
= PyString_AsString(v
);
837 self
->data
[i
] = buf
[0];
842 mmap_ass_subscript(mmap_object
*self
, PyObject
*item
, PyObject
*value
)
846 if (PyIndex_Check(item
)) {
847 Py_ssize_t i
= PyNumber_AsSsize_t(item
, PyExc_IndexError
);
850 if (i
== -1 && PyErr_Occurred())
854 if (i
< 0 || (size_t)i
> self
->size
) {
855 PyErr_SetString(PyExc_IndexError
,
856 "mmap index out of range");
860 PyErr_SetString(PyExc_TypeError
,
861 "mmap object doesn't support item deletion");
864 if (!PyString_Check(value
) || PyString_Size(value
) != 1) {
865 PyErr_SetString(PyExc_IndexError
,
866 "mmap assignment must be single-character string");
869 if (!is_writeable(self
))
871 buf
= PyString_AsString(value
);
872 self
->data
[i
] = buf
[0];
875 else if (PySlice_Check(item
)) {
876 Py_ssize_t start
, stop
, step
, slicelen
;
878 if (PySlice_GetIndicesEx((PySliceObject
*)item
,
879 self
->size
, &start
, &stop
,
880 &step
, &slicelen
) < 0) {
884 PyErr_SetString(PyExc_TypeError
,
885 "mmap object doesn't support slice deletion");
888 if (!PyString_Check(value
)) {
889 PyErr_SetString(PyExc_IndexError
,
890 "mmap slice assignment must be a string");
893 if (PyString_Size(value
) != slicelen
) {
894 PyErr_SetString(PyExc_IndexError
,
895 "mmap slice assignment is wrong size");
898 if (!is_writeable(self
))
903 else if (step
== 1) {
904 const char *buf
= PyString_AsString(value
);
908 memcpy(self
->data
+ start
, buf
, slicelen
);
913 const char *buf
= PyString_AsString(value
);
917 for (cur
= start
, i
= 0; i
< slicelen
;
919 self
->data
[cur
] = buf
[i
];
925 PyErr_SetString(PyExc_TypeError
,
926 "mmap indices must be integer");
931 static PySequenceMethods mmap_as_sequence
= {
932 (lenfunc
)mmap_length
, /*sq_length*/
933 (binaryfunc
)mmap_concat
, /*sq_concat*/
934 (ssizeargfunc
)mmap_repeat
, /*sq_repeat*/
935 (ssizeargfunc
)mmap_item
, /*sq_item*/
936 (ssizessizeargfunc
)mmap_slice
, /*sq_slice*/
937 (ssizeobjargproc
)mmap_ass_item
, /*sq_ass_item*/
938 (ssizessizeobjargproc
)mmap_ass_slice
, /*sq_ass_slice*/
941 static PyMappingMethods mmap_as_mapping
= {
942 (lenfunc
)mmap_length
,
943 (binaryfunc
)mmap_subscript
,
944 (objobjargproc
)mmap_ass_subscript
,
947 static PyBufferProcs mmap_as_buffer
= {
948 (readbufferproc
)mmap_buffer_getreadbuf
,
949 (writebufferproc
)mmap_buffer_getwritebuf
,
950 (segcountproc
)mmap_buffer_getsegcount
,
951 (charbufferproc
)mmap_buffer_getcharbuffer
,
954 static PyTypeObject mmap_object_type
= {
955 PyVarObject_HEAD_INIT(0, 0) /* patched in module init */
956 "mmap.mmap", /* tp_name */
957 sizeof(mmap_object
), /* tp_size */
960 (destructor
) mmap_object_dealloc
, /* tp_dealloc */
962 (getattrfunc
) mmap_object_getattr
, /* tp_getattr */
966 0, /* tp_as_number */
967 &mmap_as_sequence
, /*tp_as_sequence*/
968 &mmap_as_mapping
, /*tp_as_mapping*/
974 &mmap_as_buffer
, /*tp_as_buffer*/
975 Py_TPFLAGS_HAVE_GETCHARBUFFER
, /*tp_flags*/
980 /* extract the map size from the given PyObject
982 Returns -1 on error, with an appropriate Python exception raised. On
983 success, the map size is returned. */
985 _GetMapSize(PyObject
*o
, const char* param
)
989 if (PyIndex_Check(o
)) {
990 Py_ssize_t i
= PyNumber_AsSsize_t(o
, PyExc_OverflowError
);
991 if (i
==-1 && PyErr_Occurred())
994 PyErr_Format(PyExc_OverflowError
,
995 "memory mapped %s must be positive",
1002 PyErr_SetString(PyExc_TypeError
, "map size must be an integral value");
1008 new_mmap_object(PyObject
*self
, PyObject
*args
, PyObject
*kwdict
)
1014 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1015 Py_ssize_t map_size
, offset
;
1016 int fd
, flags
= MAP_SHARED
, prot
= PROT_WRITE
| PROT_READ
;
1018 int access
= (int)ACCESS_DEFAULT
;
1019 static char *keywords
[] = {"fileno", "length",
1021 "access", "offset", NULL
};
1023 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|iiiO", keywords
,
1024 &fd
, &map_size_obj
, &flags
, &prot
,
1025 &access
, &offset_obj
))
1027 map_size
= _GetMapSize(map_size_obj
, "size");
1030 offset
= _GetMapSize(offset_obj
, "offset");
1034 if ((access
!= (int)ACCESS_DEFAULT
) &&
1035 ((flags
!= MAP_SHARED
) || (prot
!= (PROT_WRITE
| PROT_READ
))))
1036 return PyErr_Format(PyExc_ValueError
,
1037 "mmap can't specify both access and flags, prot.");
1038 switch ((access_mode
)access
) {
1045 prot
= PROT_READ
| PROT_WRITE
;
1048 flags
= MAP_PRIVATE
;
1049 prot
= PROT_READ
| PROT_WRITE
;
1051 case ACCESS_DEFAULT
:
1052 /* use the specified or default values of flags and prot */
1055 return PyErr_Format(PyExc_ValueError
,
1056 "mmap invalid access parameter.");
1061 /* on OpenVMS we must ensure that all bytes are written to the file */
1066 if (fd
!= -1 && fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
1067 if (map_size
== 0) {
1068 map_size
= st
.st_size
;
1069 } else if ((size_t)offset
+ (size_t)map_size
> st
.st_size
) {
1070 PyErr_SetString(PyExc_ValueError
,
1071 "mmap length is greater than file size");
1076 m_obj
= PyObject_New(mmap_object
, &mmap_object_type
);
1077 if (m_obj
== NULL
) {return NULL
;}
1079 m_obj
->size
= (size_t) map_size
;
1080 m_obj
->pos
= (size_t) 0;
1081 m_obj
->offset
= offset
;
1084 /* Assume the caller wants to map anonymous memory.
1085 This is the same behaviour as Windows. mmap.mmap(-1, size)
1086 on both Windows and Unix map anonymous memory.
1088 #ifdef MAP_ANONYMOUS
1089 /* BSD way to map anonymous memory */
1090 flags
|= MAP_ANONYMOUS
;
1092 /* SVR4 method to map anonymous memory is to open /dev/zero */
1093 fd
= devzero
= open("/dev/zero", O_RDWR
);
1094 if (devzero
== -1) {
1096 PyErr_SetFromErrno(mmap_module_error
);
1101 m_obj
->fd
= dup(fd
);
1102 if (m_obj
->fd
== -1) {
1104 PyErr_SetFromErrno(mmap_module_error
);
1109 m_obj
->data
= mmap(NULL
, map_size
,
1113 if (devzero
!= -1) {
1117 if (m_obj
->data
== (char *)-1) {
1120 PyErr_SetFromErrno(mmap_module_error
);
1123 m_obj
->access
= (access_mode
)access
;
1124 return (PyObject
*)m_obj
;
1130 new_mmap_object(PyObject
*self
, PyObject
*args
, PyObject
*kwdict
)
1133 PyObject
*map_size_obj
= NULL
, *offset_obj
= NULL
;
1134 Py_ssize_t map_size
, offset
;
1135 DWORD off_hi
; /* upper 32 bits of offset */
1136 DWORD off_lo
; /* lower 32 bits of offset */
1137 DWORD size_hi
; /* upper 32 bits of size */
1138 DWORD size_lo
; /* lower 32 bits of size */
1143 int access
= (access_mode
)ACCESS_DEFAULT
;
1144 DWORD flProtect
, dwDesiredAccess
;
1145 static char *keywords
[] = { "fileno", "length",
1147 "access", "offset", NULL
};
1149 if (!PyArg_ParseTupleAndKeywords(args
, kwdict
, "iO|ziO", keywords
,
1150 &fileno
, &map_size_obj
,
1151 &tagname
, &access
, &offset_obj
)) {
1155 switch((access_mode
)access
) {
1157 flProtect
= PAGE_READONLY
;
1158 dwDesiredAccess
= FILE_MAP_READ
;
1160 case ACCESS_DEFAULT
: case ACCESS_WRITE
:
1161 flProtect
= PAGE_READWRITE
;
1162 dwDesiredAccess
= FILE_MAP_WRITE
;
1165 flProtect
= PAGE_WRITECOPY
;
1166 dwDesiredAccess
= FILE_MAP_COPY
;
1169 return PyErr_Format(PyExc_ValueError
,
1170 "mmap invalid access parameter.");
1173 map_size
= _GetMapSize(map_size_obj
, "size");
1176 offset
= _GetMapSize(offset_obj
, "offset");
1180 /* assume -1 and 0 both mean invalid filedescriptor
1181 to 'anonymously' map memory.
1182 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1183 XXX: Should this code be added?
1185 PyErr_Warn(PyExc_DeprecationWarning,
1186 "don't use 0 for anonymous memory");
1188 if (fileno
!= -1 && fileno
!= 0) {
1189 fh
= (HANDLE
)_get_osfhandle(fileno
);
1190 if (fh
==(HANDLE
)-1) {
1191 PyErr_SetFromErrno(mmap_module_error
);
1194 /* Win9x appears to need us seeked to zero */
1195 lseek(fileno
, 0, SEEK_SET
);
1198 m_obj
= PyObject_New(mmap_object
, &mmap_object_type
);
1201 /* Set every field to an invalid marker, so we can safely
1202 destruct the object in the face of failure */
1204 m_obj
->file_handle
= INVALID_HANDLE_VALUE
;
1205 m_obj
->map_handle
= INVALID_HANDLE_VALUE
;
1206 m_obj
->tagname
= NULL
;
1207 m_obj
->offset
= offset
;
1210 /* It is necessary to duplicate the handle, so the
1211 Python code can close it on us */
1212 if (!DuplicateHandle(
1213 GetCurrentProcess(), /* source process handle */
1214 fh
, /* handle to be duplicated */
1215 GetCurrentProcess(), /* target proc handle */
1216 (LPHANDLE
)&m_obj
->file_handle
, /* result */
1217 0, /* access - ignored due to options value */
1218 FALSE
, /* inherited by child processes? */
1219 DUPLICATE_SAME_ACCESS
)) { /* options */
1220 dwErr
= GetLastError();
1222 PyErr_SetFromWindowsErr(dwErr
);
1227 low
= GetFileSize(fh
, &high
);
1228 /* low might just happen to have the value INVALID_FILE_SIZE;
1229 so we need to check the last error also. */
1230 if (low
== INVALID_FILE_SIZE
&&
1231 (dwErr
= GetLastError()) != NO_ERROR
) {
1233 return PyErr_SetFromWindowsErr(dwErr
);
1236 #if SIZEOF_SIZE_T > 4
1237 m_obj
->size
= (((size_t)high
)<<32) + low
;
1240 /* File is too large to map completely */
1241 m_obj
->size
= (size_t)-1;
1246 m_obj
->size
= map_size
;
1250 m_obj
->size
= map_size
;
1253 /* set the initial position */
1254 m_obj
->pos
= (size_t) 0;
1256 /* set the tag name */
1257 if (tagname
!= NULL
&& *tagname
!= '\0') {
1258 m_obj
->tagname
= PyMem_Malloc(strlen(tagname
)+1);
1259 if (m_obj
->tagname
== NULL
) {
1264 strcpy(m_obj
->tagname
, tagname
);
1267 m_obj
->tagname
= NULL
;
1269 m_obj
->access
= (access_mode
)access
;
1270 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1271 * more than 4 bytes, we need to break it apart. Else (size_t
1272 * consumes 4 bytes), C doesn't define what happens if we shift
1273 * right by 32, so we need different code.
1275 #if SIZEOF_SIZE_T > 4
1276 size_hi
= (DWORD
)((offset
+ m_obj
->size
) >> 32);
1277 size_lo
= (DWORD
)((offset
+ m_obj
->size
) & 0xFFFFFFFF);
1278 off_hi
= (DWORD
)(offset
>> 32);
1279 off_lo
= (DWORD
)(offset
& 0xFFFFFFFF);
1282 size_lo
= (DWORD
)(offset
+ m_obj
->size
);
1284 off_lo
= (DWORD
)offset
;
1286 /* For files, it would be sufficient to pass 0 as size.
1287 For anonymous maps, we have to pass the size explicitly. */
1288 m_obj
->map_handle
= CreateFileMapping(m_obj
->file_handle
,
1294 if (m_obj
->map_handle
!= NULL
) {
1295 m_obj
->data
= (char *) MapViewOfFile(m_obj
->map_handle
,
1300 if (m_obj
->data
!= NULL
)
1301 return (PyObject
*)m_obj
;
1303 dwErr
= GetLastError();
1305 dwErr
= GetLastError();
1307 PyErr_SetFromWindowsErr(dwErr
);
1310 #endif /* MS_WINDOWS */
1312 /* List of functions exported by this module */
1313 static struct PyMethodDef mmap_functions
[] = {
1314 {"mmap", (PyCFunction
) new_mmap_object
,
1315 METH_VARARGS
|METH_KEYWORDS
},
1316 {NULL
, NULL
} /* Sentinel */
1320 setint(PyObject
*d
, const char *name
, long value
)
1322 PyObject
*o
= PyInt_FromLong(value
);
1323 if (o
&& PyDict_SetItemString(d
, name
, o
) == 0) {
1331 PyObject
*dict
, *module
;
1333 /* Patch the object type */
1334 Py_TYPE(&mmap_object_type
) = &PyType_Type
;
1336 module
= Py_InitModule("mmap", mmap_functions
);
1339 dict
= PyModule_GetDict(module
);
1342 mmap_module_error
= PyExc_EnvironmentError
;
1343 PyDict_SetItemString(dict
, "error", mmap_module_error
);
1345 setint(dict
, "PROT_EXEC", PROT_EXEC
);
1348 setint(dict
, "PROT_READ", PROT_READ
);
1351 setint(dict
, "PROT_WRITE", PROT_WRITE
);
1355 setint(dict
, "MAP_SHARED", MAP_SHARED
);
1358 setint(dict
, "MAP_PRIVATE", MAP_PRIVATE
);
1360 #ifdef MAP_DENYWRITE
1361 setint(dict
, "MAP_DENYWRITE", MAP_DENYWRITE
);
1363 #ifdef MAP_EXECUTABLE
1364 setint(dict
, "MAP_EXECUTABLE", MAP_EXECUTABLE
);
1366 #ifdef MAP_ANONYMOUS
1367 setint(dict
, "MAP_ANON", MAP_ANONYMOUS
);
1368 setint(dict
, "MAP_ANONYMOUS", MAP_ANONYMOUS
);
1371 setint(dict
, "PAGESIZE", (long)my_getpagesize());
1373 setint(dict
, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1375 setint(dict
, "ACCESS_READ", ACCESS_READ
);
1376 setint(dict
, "ACCESS_WRITE", ACCESS_WRITE
);
1377 setint(dict
, "ACCESS_COPY", ACCESS_COPY
);