Add better error reporting for MemoryErrors caused by str->float conversions.
[python.git] / Modules / mmapmodule.c
blob5e0b3ad6211bed5cc986405a1fd5ba670975a9fd
1 /*
2 / Author: Sam Rushing <rushing@nightmare.com>
3 / Hacked for Unix by AMK
4 / $Id$
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
22 #include <Python.h>
24 #ifndef MS_WINDOWS
25 #define UNIX
26 #endif
28 #ifdef MS_WINDOWS
29 #include <windows.h>
30 static int
31 my_getpagesize(void)
33 SYSTEM_INFO si;
34 GetSystemInfo(&si);
35 return si.dwPageSize;
38 static int
39 my_getallocationgranularity (void)
42 SYSTEM_INFO si;
43 GetSystemInfo(&si);
44 return si.dwAllocationGranularity;
47 #endif
49 #ifdef UNIX
50 #include <sys/mman.h>
51 #include <sys/stat.h>
53 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
54 static int
55 my_getpagesize(void)
57 return sysconf(_SC_PAGESIZE);
60 #define my_getallocationgranularity my_getpagesize
61 #else
62 #define my_getpagesize getpagesize
63 #endif
65 #endif /* UNIX */
67 #include <string.h>
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
76 #endif
78 static PyObject *mmap_module_error;
80 typedef enum
82 ACCESS_DEFAULT,
83 ACCESS_READ,
84 ACCESS_WRITE,
85 ACCESS_COPY
86 } access_mode;
88 typedef struct {
89 PyObject_HEAD
90 char * data;
91 size_t size;
92 size_t pos; /* relative to offset */
93 size_t offset;
95 #ifdef MS_WINDOWS
96 HANDLE map_handle;
97 HANDLE file_handle;
98 char * tagname;
99 #endif
101 #ifdef UNIX
102 int fd;
103 #endif
105 access_mode access;
106 } mmap_object;
109 static void
110 mmap_object_dealloc(mmap_object *m_obj)
112 #ifdef MS_WINDOWS
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);
119 if (m_obj->tagname)
120 PyMem_Free(m_obj->tagname);
121 #endif /* MS_WINDOWS */
123 #ifdef UNIX
124 if (m_obj->fd >= 0)
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);
130 #endif /* UNIX */
132 Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
135 static PyObject *
136 mmap_close_method(mmap_object *self, PyObject *unused)
138 #ifdef MS_WINDOWS
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
143 again.
144 TODO - should we check for errors in the close operations???
146 if (self->data != NULL) {
147 UnmapViewOfFile(self->data);
148 self->data = NULL;
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 */
160 #ifdef UNIX
161 if (0 <= self->fd)
162 (void) close(self->fd);
163 self->fd = -1;
164 if (self->data != NULL) {
165 munmap(self->data, self->size);
166 self->data = NULL;
168 #endif
170 Py_INCREF(Py_None);
171 return Py_None;
174 #ifdef MS_WINDOWS
175 #define CHECK_VALID(err) \
176 do { \
177 if (self->map_handle == NULL) { \
178 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
179 return err; \
181 } while (0)
182 #endif /* MS_WINDOWS */
184 #ifdef UNIX
185 #define CHECK_VALID(err) \
186 do { \
187 if (self->data == NULL) { \
188 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
189 return err; \
191 } while (0)
192 #endif /* UNIX */
194 static PyObject *
195 mmap_read_byte_method(mmap_object *self,
196 PyObject *unused)
198 CHECK_VALID(NULL);
199 if (self->pos < self->size) {
200 char value = self->data[self->pos];
201 self->pos += 1;
202 return Py_BuildValue("c", value);
203 } else {
204 PyErr_SetString(PyExc_ValueError, "read byte out of range");
205 return NULL;
209 static PyObject *
210 mmap_read_line_method(mmap_object *self,
211 PyObject *unused)
213 char *start = self->data+self->pos;
214 char *eof = self->data+self->size;
215 char *eol;
216 PyObject *result;
218 CHECK_VALID(NULL);
220 eol = memchr(start, '\n', self->size - self->pos);
221 if (!eol)
222 eol = eof;
223 else
224 ++eol; /* we're interested in the position after the
225 newline. */
226 result = PyString_FromStringAndSize(start, (eol - start));
227 self->pos += (eol - start);
228 return result;
231 static PyObject *
232 mmap_read_method(mmap_object *self,
233 PyObject *args)
235 Py_ssize_t num_bytes, n;
236 PyObject *result;
238 CHECK_VALID(NULL);
239 if (!PyArg_ParseTuple(args, "n:read", &num_bytes))
240 return(NULL);
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.
251 if (n < 0)
252 n = PY_SSIZE_T_MAX;
253 if (num_bytes < 0 || num_bytes > n) {
254 num_bytes = n;
256 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
257 self->pos += num_bytes;
258 return result;
261 static PyObject *
262 mmap_gfind(mmap_object *self,
263 PyObject *args,
264 int reverse)
266 Py_ssize_t start = self->pos;
267 Py_ssize_t end = self->size;
268 const char *needle;
269 Py_ssize_t len;
271 CHECK_VALID(NULL);
272 if (!PyArg_ParseTuple(args, reverse ? "s#|nn:rfind" : "s#|nn:find",
273 &needle, &len, &start, &end)) {
274 return NULL;
275 } else {
276 const char *p, *start_p, *end_p;
277 int sign = reverse ? -1 : 1;
279 if (start < 0)
280 start += self->size;
281 if (start < 0)
282 start = 0;
283 else if ((size_t)start > self->size)
284 start = self->size;
286 if (end < 0)
287 end += self->size;
288 if (end < 0)
289 end = 0;
290 else if ((size_t)end > self->size)
291 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) {
298 Py_ssize_t i;
299 for (i = 0; i < len && needle[i] == p[i]; ++i)
300 /* nothing */;
301 if (i == len) {
302 return PyInt_FromSsize_t(p - self->data);
305 return PyInt_FromLong(-1);
309 static PyObject *
310 mmap_find_method(mmap_object *self,
311 PyObject *args)
313 return mmap_gfind(self, args, 0);
316 static PyObject *
317 mmap_rfind_method(mmap_object *self,
318 PyObject *args)
320 return mmap_gfind(self, args, 1);
323 static int
324 is_writeable(mmap_object *self)
326 if (self->access != ACCESS_READ)
327 return 1;
328 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
329 return 0;
332 static int
333 is_resizeable(mmap_object *self)
335 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
336 return 1;
337 PyErr_Format(PyExc_TypeError,
338 "mmap can't resize a readonly or copy-on-write memory map.");
339 return 0;
343 static PyObject *
344 mmap_write_method(mmap_object *self,
345 PyObject *args)
347 Py_ssize_t length;
348 char *data;
350 CHECK_VALID(NULL);
351 if (!PyArg_ParseTuple(args, "s#:write", &data, &length))
352 return(NULL);
354 if (!is_writeable(self))
355 return NULL;
357 if ((self->pos + length) > self->size) {
358 PyErr_SetString(PyExc_ValueError, "data out of range");
359 return NULL;
361 memcpy(self->data+self->pos, data, length);
362 self->pos = self->pos+length;
363 Py_INCREF(Py_None);
364 return Py_None;
367 static PyObject *
368 mmap_write_byte_method(mmap_object *self,
369 PyObject *args)
371 char value;
373 CHECK_VALID(NULL);
374 if (!PyArg_ParseTuple(args, "c:write_byte", &value))
375 return(NULL);
377 if (!is_writeable(self))
378 return NULL;
380 if (self->pos < self->size) {
381 *(self->data+self->pos) = value;
382 self->pos += 1;
383 Py_INCREF(Py_None);
384 return Py_None;
386 else {
387 PyErr_SetString(PyExc_ValueError, "write byte out of range");
388 return NULL;
392 static PyObject *
393 mmap_size_method(mmap_object *self,
394 PyObject *unused)
396 CHECK_VALID(NULL);
398 #ifdef MS_WINDOWS
399 if (self->file_handle != INVALID_HANDLE_VALUE) {
400 DWORD low,high;
401 PY_LONG_LONG size;
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);
414 } else {
415 return PyInt_FromSsize_t(self->size);
417 #endif /* MS_WINDOWS */
419 #ifdef UNIX
421 struct stat buf;
422 if (-1 == fstat(self->fd, &buf)) {
423 PyErr_SetFromErrno(mmap_module_error);
424 return NULL;
426 return PyInt_FromSsize_t(buf.st_size);
428 #endif /* UNIX */
431 /* This assumes that you want the entire file mapped,
432 / and when recreating the map will make the new file
433 / have the new size
435 / Is this really necessary? This could easily be done
436 / from python by just closing and re-opening with the
437 / new size?
440 static PyObject *
441 mmap_resize_method(mmap_object *self,
442 PyObject *args)
444 Py_ssize_t new_size;
445 CHECK_VALID(NULL);
446 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
447 !is_resizeable(self)) {
448 return NULL;
449 #ifdef MS_WINDOWS
450 } else {
451 DWORD dwErrCode = 0;
452 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
453 /* First, unmap the file view */
454 UnmapViewOfFile(self->data);
455 self->data = NULL;
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);
465 #else
466 newSizeHigh = 0;
467 newSizeLow = (DWORD)(self->offset + new_size);
468 off_hi = 0;
469 off_lo = (DWORD)self->offset;
470 #endif
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(
477 self->file_handle,
478 NULL,
479 PAGE_READWRITE,
482 self->tagname);
483 if (self->map_handle != NULL) {
484 self->data = (char *) MapViewOfFile(self->map_handle,
485 FILE_MAP_WRITE,
486 off_hi,
487 off_lo,
488 new_size);
489 if (self->data != NULL) {
490 self->size = new_size;
491 Py_INCREF(Py_None);
492 return Py_None;
493 } else {
494 dwErrCode = GetLastError();
495 CloseHandle(self->map_handle);
496 self->map_handle = NULL;
498 } else {
499 dwErrCode = GetLastError();
501 PyErr_SetFromWindowsErr(dwErrCode);
502 return NULL;
503 #endif /* MS_WINDOWS */
505 #ifdef UNIX
506 #ifndef HAVE_MREMAP
507 } else {
508 PyErr_SetString(PyExc_SystemError,
509 "mmap: resizing not available--no mremap()");
510 return NULL;
511 #else
512 } else {
513 void *newmap;
515 if (ftruncate(self->fd, self->offset + new_size) == -1) {
516 PyErr_SetFromErrno(mmap_module_error);
517 return NULL;
520 #ifdef MREMAP_MAYMOVE
521 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
522 #else
523 #if defined(__NetBSD__)
524 newmap = mremap(self->data, self->size, self->data, new_size, 0);
525 #else
526 newmap = mremap(self->data, self->size, new_size, 0);
527 #endif /* __NetBSD__ */
528 #endif
529 if (newmap == (void *)-1)
531 PyErr_SetFromErrno(mmap_module_error);
532 return NULL;
534 self->data = newmap;
535 self->size = new_size;
536 Py_INCREF(Py_None);
537 return Py_None;
538 #endif /* HAVE_MREMAP */
539 #endif /* UNIX */
543 static PyObject *
544 mmap_tell_method(mmap_object *self, PyObject *unused)
546 CHECK_VALID(NULL);
547 return PyInt_FromSize_t(self->pos);
550 static PyObject *
551 mmap_flush_method(mmap_object *self, PyObject *args)
553 Py_ssize_t offset = 0;
554 Py_ssize_t size = self->size;
555 CHECK_VALID(NULL);
556 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
557 return NULL;
558 if ((size_t)(offset + size) > self->size) {
559 PyErr_SetString(PyExc_ValueError, "flush values out of range");
560 return NULL;
562 #ifdef MS_WINDOWS
563 return PyInt_FromLong((long) FlushViewOfFile(self->data+offset, size));
564 #elif defined(UNIX)
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);
569 return NULL;
571 return PyInt_FromLong(0);
572 #else
573 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
574 return NULL;
575 #endif
578 static PyObject *
579 mmap_seek_method(mmap_object *self, PyObject *args)
581 Py_ssize_t dist;
582 int how=0;
583 CHECK_VALID(NULL);
584 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
585 return NULL;
586 else {
587 size_t where;
588 switch (how) {
589 case 0: /* relative to start */
590 if (dist < 0)
591 goto onoutofrange;
592 where = dist;
593 break;
594 case 1: /* relative to current position */
595 if ((Py_ssize_t)self->pos + dist < 0)
596 goto onoutofrange;
597 where = self->pos + dist;
598 break;
599 case 2: /* relative to end */
600 if ((Py_ssize_t)self->size + dist < 0)
601 goto onoutofrange;
602 where = self->size + dist;
603 break;
604 default:
605 PyErr_SetString(PyExc_ValueError, "unknown seek type");
606 return NULL;
608 if (where > self->size)
609 goto onoutofrange;
610 self->pos = where;
611 Py_INCREF(Py_None);
612 return Py_None;
615 onoutofrange:
616 PyErr_SetString(PyExc_ValueError, "seek out of range");
617 return NULL;
620 static PyObject *
621 mmap_move_method(mmap_object *self, PyObject *args)
623 unsigned long dest, src, cnt;
624 CHECK_VALID(NULL);
625 if (!PyArg_ParseTuple(args, "kkk:move", &dest, &src, &cnt) ||
626 !is_writeable(self)) {
627 return NULL;
628 } else {
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");
635 return NULL;
637 memmove(self->data+dest, self->data+src, cnt);
638 Py_INCREF(Py_None);
639 return Py_None;
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 */
663 static Py_ssize_t
664 mmap_buffer_getreadbuf(mmap_object *self, Py_ssize_t index, const void **ptr)
666 CHECK_VALID(-1);
667 if (index != 0) {
668 PyErr_SetString(PyExc_SystemError,
669 "Accessing non-existent mmap segment");
670 return -1;
672 *ptr = self->data;
673 return self->size;
676 static Py_ssize_t
677 mmap_buffer_getwritebuf(mmap_object *self, Py_ssize_t index, const void **ptr)
679 CHECK_VALID(-1);
680 if (index != 0) {
681 PyErr_SetString(PyExc_SystemError,
682 "Accessing non-existent mmap segment");
683 return -1;
685 if (!is_writeable(self))
686 return -1;
687 *ptr = self->data;
688 return self->size;
691 static Py_ssize_t
692 mmap_buffer_getsegcount(mmap_object *self, Py_ssize_t *lenp)
694 CHECK_VALID(-1);
695 if (lenp)
696 *lenp = self->size;
697 return 1;
700 static Py_ssize_t
701 mmap_buffer_getcharbuffer(mmap_object *self, Py_ssize_t index, const void **ptr)
703 if (index != 0) {
704 PyErr_SetString(PyExc_SystemError,
705 "accessing non-existent buffer segment");
706 return -1;
708 *ptr = (const char *)self->data;
709 return self->size;
712 static Py_ssize_t
713 mmap_length(mmap_object *self)
715 CHECK_VALID(-1);
716 return self->size;
719 static PyObject *
720 mmap_item(mmap_object *self, Py_ssize_t i)
722 CHECK_VALID(NULL);
723 if (i < 0 || (size_t)i >= self->size) {
724 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
725 return NULL;
727 return PyString_FromStringAndSize(self->data + i, 1);
730 static PyObject *
731 mmap_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh)
733 CHECK_VALID(NULL);
734 if (ilow < 0)
735 ilow = 0;
736 else if ((size_t)ilow > self->size)
737 ilow = self->size;
738 if (ihigh < 0)
739 ihigh = 0;
740 if (ihigh < ilow)
741 ihigh = ilow;
742 else if ((size_t)ihigh > self->size)
743 ihigh = self->size;
745 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
748 static PyObject *
749 mmap_subscript(mmap_object *self, PyObject *item)
751 CHECK_VALID(NULL);
752 if (PyIndex_Check(item)) {
753 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
754 if (i == -1 && PyErr_Occurred())
755 return NULL;
756 if (i < 0)
757 i += self->size;
758 if (i < 0 || (size_t)i >= self->size) {
759 PyErr_SetString(PyExc_IndexError,
760 "mmap index out of range");
761 return NULL;
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) {
770 return NULL;
773 if (slicelen <= 0)
774 return PyString_FromStringAndSize("", 0);
775 else if (step == 1)
776 return PyString_FromStringAndSize(self->data + start,
777 slicelen);
778 else {
779 char *result_buf = (char *)PyMem_Malloc(slicelen);
780 Py_ssize_t cur, i;
781 PyObject *result;
783 if (result_buf == NULL)
784 return PyErr_NoMemory();
785 for (cur = start, i = 0; i < slicelen;
786 cur += step, i++) {
787 result_buf[i] = self->data[cur];
789 result = PyString_FromStringAndSize(result_buf,
790 slicelen);
791 PyMem_Free(result_buf);
792 return result;
795 else {
796 PyErr_SetString(PyExc_TypeError,
797 "mmap indices must be integers");
798 return NULL;
802 static PyObject *
803 mmap_concat(mmap_object *self, PyObject *bb)
805 CHECK_VALID(NULL);
806 PyErr_SetString(PyExc_SystemError,
807 "mmaps don't support concatenation");
808 return NULL;
811 static PyObject *
812 mmap_repeat(mmap_object *self, Py_ssize_t n)
814 CHECK_VALID(NULL);
815 PyErr_SetString(PyExc_SystemError,
816 "mmaps don't support repeat operation");
817 return NULL;
820 static int
821 mmap_ass_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
823 const char *buf;
825 CHECK_VALID(-1);
826 if (ilow < 0)
827 ilow = 0;
828 else if ((size_t)ilow > self->size)
829 ilow = self->size;
830 if (ihigh < 0)
831 ihigh = 0;
832 if (ihigh < ilow)
833 ihigh = ilow;
834 else if ((size_t)ihigh > self->size)
835 ihigh = self->size;
837 if (v == NULL) {
838 PyErr_SetString(PyExc_TypeError,
839 "mmap object doesn't support slice deletion");
840 return -1;
842 if (! (PyString_Check(v)) ) {
843 PyErr_SetString(PyExc_IndexError,
844 "mmap slice assignment must be a string");
845 return -1;
847 if (PyString_Size(v) != (ihigh - ilow)) {
848 PyErr_SetString(PyExc_IndexError,
849 "mmap slice assignment is wrong size");
850 return -1;
852 if (!is_writeable(self))
853 return -1;
854 buf = PyString_AsString(v);
855 memcpy(self->data + ilow, buf, ihigh-ilow);
856 return 0;
859 static int
860 mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
862 const char *buf;
864 CHECK_VALID(-1);
865 if (i < 0 || (size_t)i >= self->size) {
866 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
867 return -1;
869 if (v == NULL) {
870 PyErr_SetString(PyExc_TypeError,
871 "mmap object doesn't support item deletion");
872 return -1;
874 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
875 PyErr_SetString(PyExc_IndexError,
876 "mmap assignment must be single-character string");
877 return -1;
879 if (!is_writeable(self))
880 return -1;
881 buf = PyString_AsString(v);
882 self->data[i] = buf[0];
883 return 0;
886 static int
887 mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
889 CHECK_VALID(-1);
891 if (PyIndex_Check(item)) {
892 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
893 const char *buf;
895 if (i == -1 && PyErr_Occurred())
896 return -1;
897 if (i < 0)
898 i += self->size;
899 if (i < 0 || (size_t)i >= self->size) {
900 PyErr_SetString(PyExc_IndexError,
901 "mmap index out of range");
902 return -1;
904 if (value == NULL) {
905 PyErr_SetString(PyExc_TypeError,
906 "mmap object doesn't support item deletion");
907 return -1;
909 if (!PyString_Check(value) || PyString_Size(value) != 1) {
910 PyErr_SetString(PyExc_IndexError,
911 "mmap assignment must be single-character string");
912 return -1;
914 if (!is_writeable(self))
915 return -1;
916 buf = PyString_AsString(value);
917 self->data[i] = buf[0];
918 return 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) {
926 return -1;
928 if (value == NULL) {
929 PyErr_SetString(PyExc_TypeError,
930 "mmap object doesn't support slice deletion");
931 return -1;
933 if (!PyString_Check(value)) {
934 PyErr_SetString(PyExc_IndexError,
935 "mmap slice assignment must be a string");
936 return -1;
938 if (PyString_Size(value) != slicelen) {
939 PyErr_SetString(PyExc_IndexError,
940 "mmap slice assignment is wrong size");
941 return -1;
943 if (!is_writeable(self))
944 return -1;
946 if (slicelen == 0)
947 return 0;
948 else if (step == 1) {
949 const char *buf = PyString_AsString(value);
951 if (buf == NULL)
952 return -1;
953 memcpy(self->data + start, buf, slicelen);
954 return 0;
956 else {
957 Py_ssize_t cur, i;
958 const char *buf = PyString_AsString(value);
960 if (buf == NULL)
961 return -1;
962 for (cur = start, i = 0; i < slicelen;
963 cur += step, i++) {
964 self->data[cur] = buf[i];
966 return 0;
969 else {
970 PyErr_SetString(PyExc_TypeError,
971 "mmap indices must be integer");
972 return -1;
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,
999 static PyObject *
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 */
1031 /* methods */
1032 (destructor) mmap_object_dealloc, /* tp_dealloc */
1033 0, /* tp_print */
1034 0, /* tp_getattr */
1035 0, /* tp_setattr */
1036 0, /* tp_compare */
1037 0, /* tp_repr */
1038 0, /* tp_as_number */
1039 &mmap_as_sequence, /*tp_as_sequence*/
1040 &mmap_as_mapping, /*tp_as_mapping*/
1041 0, /*tp_hash*/
1042 0, /*tp_call*/
1043 0, /*tp_str*/
1044 PyObject_GenericGetAttr, /*tp_getattro*/
1045 0, /*tp_setattro*/
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 */
1050 0, /* tp_clear */
1051 0, /* tp_richcompare */
1052 0, /* tp_weaklistoffset */
1053 0, /* tp_iter */
1054 0, /* tp_iternext */
1055 mmap_object_methods, /* tp_methods */
1056 0, /* tp_members */
1057 0, /* tp_getset */
1058 0, /* tp_base */
1059 0, /* tp_dict */
1060 0, /* tp_descr_get */
1061 0, /* tp_descr_set */
1062 0, /* tp_dictoffset */
1063 0, /* tp_init */
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. */
1074 static Py_ssize_t
1075 _GetMapSize(PyObject *o, const char* param)
1077 if (o == NULL)
1078 return 0;
1079 if (PyIndex_Check(o)) {
1080 Py_ssize_t i = PyNumber_AsSsize_t(o, PyExc_OverflowError);
1081 if (i==-1 && PyErr_Occurred())
1082 return -1;
1083 if (i < 0) {
1084 PyErr_Format(PyExc_OverflowError,
1085 "memory mapped %s must be positive",
1086 param);
1087 return -1;
1089 return i;
1092 PyErr_SetString(PyExc_TypeError, "map size must be an integral value");
1093 return -1;
1096 #ifdef UNIX
1097 static PyObject *
1098 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1100 #ifdef HAVE_FSTAT
1101 struct stat st;
1102 #endif
1103 mmap_object *m_obj;
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;
1107 int devzero = -1;
1108 int access = (int)ACCESS_DEFAULT;
1109 static char *keywords[] = {"fileno", "length",
1110 "flags", "prot",
1111 "access", "offset", NULL};
1113 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iiiO", keywords,
1114 &fd, &map_size_obj, &flags, &prot,
1115 &access, &offset_obj))
1116 return NULL;
1117 map_size = _GetMapSize(map_size_obj, "size");
1118 if (map_size < 0)
1119 return NULL;
1120 offset = _GetMapSize(offset_obj, "offset");
1121 if (offset < 0)
1122 return NULL;
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) {
1129 case ACCESS_READ:
1130 flags = MAP_SHARED;
1131 prot = PROT_READ;
1132 break;
1133 case ACCESS_WRITE:
1134 flags = MAP_SHARED;
1135 prot = PROT_READ | PROT_WRITE;
1136 break;
1137 case ACCESS_COPY:
1138 flags = MAP_PRIVATE;
1139 prot = PROT_READ | PROT_WRITE;
1140 break;
1141 case ACCESS_DEFAULT:
1142 /* use the specified or default values of flags and prot */
1143 break;
1144 default:
1145 return PyErr_Format(PyExc_ValueError,
1146 "mmap invalid access parameter.");
1149 if (prot == PROT_READ) {
1150 access = ACCESS_READ;
1153 #ifdef HAVE_FSTAT
1154 # ifdef __VMS
1155 /* on OpenVMS we must ensure that all bytes are written to the file */
1156 if (fd != -1) {
1157 fsync(fd);
1159 # endif
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");
1166 return NULL;
1169 #endif
1170 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1171 if (m_obj == NULL) {return NULL;}
1172 m_obj->data = NULL;
1173 m_obj->size = (size_t) map_size;
1174 m_obj->pos = (size_t) 0;
1175 m_obj->offset = offset;
1176 if (fd == -1) {
1177 m_obj->fd = -1;
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;
1185 #else
1186 /* SVR4 method to map anonymous memory is to open /dev/zero */
1187 fd = devzero = open("/dev/zero", O_RDWR);
1188 if (devzero == -1) {
1189 Py_DECREF(m_obj);
1190 PyErr_SetFromErrno(mmap_module_error);
1191 return NULL;
1193 #endif
1194 } else {
1195 m_obj->fd = dup(fd);
1196 if (m_obj->fd == -1) {
1197 Py_DECREF(m_obj);
1198 PyErr_SetFromErrno(mmap_module_error);
1199 return NULL;
1203 m_obj->data = mmap(NULL, map_size,
1204 prot, flags,
1205 fd, offset);
1207 if (devzero != -1) {
1208 close(devzero);
1211 if (m_obj->data == (char *)-1) {
1212 m_obj->data = NULL;
1213 Py_DECREF(m_obj);
1214 PyErr_SetFromErrno(mmap_module_error);
1215 return NULL;
1217 m_obj->access = (access_mode)access;
1218 return (PyObject *)m_obj;
1220 #endif /* UNIX */
1222 #ifdef MS_WINDOWS
1223 static PyObject *
1224 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1226 mmap_object *m_obj;
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 */
1233 char *tagname = "";
1234 DWORD dwErr = 0;
1235 int fileno;
1236 HANDLE fh = 0;
1237 int access = (access_mode)ACCESS_DEFAULT;
1238 DWORD flProtect, dwDesiredAccess;
1239 static char *keywords[] = { "fileno", "length",
1240 "tagname",
1241 "access", "offset", NULL };
1243 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|ziO", keywords,
1244 &fileno, &map_size_obj,
1245 &tagname, &access, &offset_obj)) {
1246 return NULL;
1249 switch((access_mode)access) {
1250 case ACCESS_READ:
1251 flProtect = PAGE_READONLY;
1252 dwDesiredAccess = FILE_MAP_READ;
1253 break;
1254 case ACCESS_DEFAULT: case ACCESS_WRITE:
1255 flProtect = PAGE_READWRITE;
1256 dwDesiredAccess = FILE_MAP_WRITE;
1257 break;
1258 case ACCESS_COPY:
1259 flProtect = PAGE_WRITECOPY;
1260 dwDesiredAccess = FILE_MAP_COPY;
1261 break;
1262 default:
1263 return PyErr_Format(PyExc_ValueError,
1264 "mmap invalid access parameter.");
1267 map_size = _GetMapSize(map_size_obj, "size");
1268 if (map_size < 0)
1269 return NULL;
1270 offset = _GetMapSize(offset_obj, "offset");
1271 if (offset < 0)
1272 return NULL;
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?
1278 if (fileno == 0)
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);
1286 return NULL;
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);
1293 if (m_obj == NULL)
1294 return NULL;
1295 /* Set every field to an invalid marker, so we can safely
1296 destruct the object in the face of failure */
1297 m_obj->data = NULL;
1298 m_obj->file_handle = INVALID_HANDLE_VALUE;
1299 m_obj->map_handle = NULL;
1300 m_obj->tagname = NULL;
1301 m_obj->offset = offset;
1303 if (fh) {
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();
1315 Py_DECREF(m_obj);
1316 PyErr_SetFromWindowsErr(dwErr);
1317 return NULL;
1319 if (!map_size) {
1320 DWORD low,high;
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) {
1326 Py_DECREF(m_obj);
1327 return PyErr_SetFromWindowsErr(dwErr);
1330 #if SIZEOF_SIZE_T > 4
1331 m_obj->size = (((size_t)high)<<32) + low;
1332 #else
1333 if (high)
1334 /* File is too large to map completely */
1335 m_obj->size = (size_t)-1;
1336 else
1337 m_obj->size = low;
1338 #endif
1339 } else {
1340 m_obj->size = map_size;
1343 else {
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) {
1354 PyErr_NoMemory();
1355 Py_DECREF(m_obj);
1356 return NULL;
1358 strcpy(m_obj->tagname, tagname);
1360 else
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);
1374 #else
1375 size_hi = 0;
1376 size_lo = (DWORD)(offset + m_obj->size);
1377 off_hi = 0;
1378 off_lo = (DWORD)offset;
1379 #endif
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,
1383 NULL,
1384 flProtect,
1385 size_hi,
1386 size_lo,
1387 m_obj->tagname);
1388 if (m_obj->map_handle != NULL) {
1389 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1390 dwDesiredAccess,
1391 off_hi,
1392 off_lo,
1393 m_obj->size);
1394 if (m_obj->data != NULL)
1395 return (PyObject *)m_obj;
1396 else {
1397 dwErr = GetLastError();
1398 CloseHandle(m_obj->map_handle);
1399 m_obj->map_handle = NULL;
1401 } else
1402 dwErr = GetLastError();
1403 Py_DECREF(m_obj);
1404 PyErr_SetFromWindowsErr(dwErr);
1405 return NULL;
1407 #endif /* MS_WINDOWS */
1409 static void
1410 setint(PyObject *d, const char *name, long value)
1412 PyObject *o = PyInt_FromLong(value);
1413 if (o && PyDict_SetItemString(d, name, o) == 0) {
1414 Py_DECREF(o);
1418 PyMODINIT_FUNC
1419 initmmap(void)
1421 PyObject *dict, *module;
1423 if (PyType_Ready(&mmap_object_type) < 0)
1424 return;
1426 module = Py_InitModule("mmap", NULL);
1427 if (module == NULL)
1428 return;
1429 dict = PyModule_GetDict(module);
1430 if (!dict)
1431 return;
1432 mmap_module_error = PyErr_NewException("mmap.error",
1433 PyExc_EnvironmentError , NULL);
1434 if (mmap_module_error == NULL)
1435 return;
1436 PyDict_SetItemString(dict, "error", mmap_module_error);
1437 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
1438 #ifdef PROT_EXEC
1439 setint(dict, "PROT_EXEC", PROT_EXEC);
1440 #endif
1441 #ifdef PROT_READ
1442 setint(dict, "PROT_READ", PROT_READ);
1443 #endif
1444 #ifdef PROT_WRITE
1445 setint(dict, "PROT_WRITE", PROT_WRITE);
1446 #endif
1448 #ifdef MAP_SHARED
1449 setint(dict, "MAP_SHARED", MAP_SHARED);
1450 #endif
1451 #ifdef MAP_PRIVATE
1452 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
1453 #endif
1454 #ifdef MAP_DENYWRITE
1455 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
1456 #endif
1457 #ifdef MAP_EXECUTABLE
1458 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
1459 #endif
1460 #ifdef MAP_ANONYMOUS
1461 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1462 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
1463 #endif
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);