Issue 6305: Clarify error message for large arguments to itertools.islice().
[python.git] / Modules / mmapmodule.c
blobe1970cbb53be36d3b3eda6713d17acfb4ad260d9
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;
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 if (num_bytes > self->size - self->pos) {
244 num_bytes -= (self->pos+num_bytes) - self->size;
246 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
247 self->pos += num_bytes;
248 return result;
251 static PyObject *
252 mmap_gfind(mmap_object *self,
253 PyObject *args,
254 int reverse)
256 Py_ssize_t start = self->pos;
257 Py_ssize_t end = self->size;
258 const char *needle;
259 Py_ssize_t len;
261 CHECK_VALID(NULL);
262 if (!PyArg_ParseTuple(args, reverse ? "s#|nn:rfind" : "s#|nn:find",
263 &needle, &len, &start, &end)) {
264 return NULL;
265 } else {
266 const char *p, *start_p, *end_p;
267 int sign = reverse ? -1 : 1;
269 if (start < 0)
270 start += self->size;
271 if (start < 0)
272 start = 0;
273 else if ((size_t)start > self->size)
274 start = self->size;
276 if (end < 0)
277 end += self->size;
278 if (end < 0)
279 end = 0;
280 else if ((size_t)end > self->size)
281 end = self->size;
283 start_p = self->data + start;
284 end_p = self->data + end;
286 for (p = (reverse ? end_p - len : start_p);
287 (p >= start_p) && (p + len <= end_p); p += sign) {
288 Py_ssize_t i;
289 for (i = 0; i < len && needle[i] == p[i]; ++i)
290 /* nothing */;
291 if (i == len) {
292 return PyInt_FromSsize_t(p - self->data);
295 return PyInt_FromLong(-1);
299 static PyObject *
300 mmap_find_method(mmap_object *self,
301 PyObject *args)
303 return mmap_gfind(self, args, 0);
306 static PyObject *
307 mmap_rfind_method(mmap_object *self,
308 PyObject *args)
310 return mmap_gfind(self, args, 1);
313 static int
314 is_writeable(mmap_object *self)
316 if (self->access != ACCESS_READ)
317 return 1;
318 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
319 return 0;
322 static int
323 is_resizeable(mmap_object *self)
325 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
326 return 1;
327 PyErr_Format(PyExc_TypeError,
328 "mmap can't resize a readonly or copy-on-write memory map.");
329 return 0;
333 static PyObject *
334 mmap_write_method(mmap_object *self,
335 PyObject *args)
337 Py_ssize_t length;
338 char *data;
340 CHECK_VALID(NULL);
341 if (!PyArg_ParseTuple(args, "s#:write", &data, &length))
342 return(NULL);
344 if (!is_writeable(self))
345 return NULL;
347 if ((self->pos + length) > self->size) {
348 PyErr_SetString(PyExc_ValueError, "data out of range");
349 return NULL;
351 memcpy(self->data+self->pos, data, length);
352 self->pos = self->pos+length;
353 Py_INCREF(Py_None);
354 return Py_None;
357 static PyObject *
358 mmap_write_byte_method(mmap_object *self,
359 PyObject *args)
361 char value;
363 CHECK_VALID(NULL);
364 if (!PyArg_ParseTuple(args, "c:write_byte", &value))
365 return(NULL);
367 if (!is_writeable(self))
368 return NULL;
370 if (self->pos < self->size) {
371 *(self->data+self->pos) = value;
372 self->pos += 1;
373 Py_INCREF(Py_None);
374 return Py_None;
376 else {
377 PyErr_SetString(PyExc_ValueError, "write byte out of range");
378 return NULL;
382 static PyObject *
383 mmap_size_method(mmap_object *self,
384 PyObject *unused)
386 CHECK_VALID(NULL);
388 #ifdef MS_WINDOWS
389 if (self->file_handle != INVALID_HANDLE_VALUE) {
390 DWORD low,high;
391 PY_LONG_LONG size;
392 low = GetFileSize(self->file_handle, &high);
393 if (low == INVALID_FILE_SIZE) {
394 /* It might be that the function appears to have failed,
395 when indeed its size equals INVALID_FILE_SIZE */
396 DWORD error = GetLastError();
397 if (error != NO_ERROR)
398 return PyErr_SetFromWindowsErr(error);
400 if (!high && low < LONG_MAX)
401 return PyInt_FromLong((long)low);
402 size = (((PY_LONG_LONG)high)<<32) + low;
403 return PyLong_FromLongLong(size);
404 } else {
405 return PyInt_FromSsize_t(self->size);
407 #endif /* MS_WINDOWS */
409 #ifdef UNIX
411 struct stat buf;
412 if (-1 == fstat(self->fd, &buf)) {
413 PyErr_SetFromErrno(mmap_module_error);
414 return NULL;
416 return PyInt_FromSsize_t(buf.st_size);
418 #endif /* UNIX */
421 /* This assumes that you want the entire file mapped,
422 / and when recreating the map will make the new file
423 / have the new size
425 / Is this really necessary? This could easily be done
426 / from python by just closing and re-opening with the
427 / new size?
430 static PyObject *
431 mmap_resize_method(mmap_object *self,
432 PyObject *args)
434 Py_ssize_t new_size;
435 CHECK_VALID(NULL);
436 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
437 !is_resizeable(self)) {
438 return NULL;
439 #ifdef MS_WINDOWS
440 } else {
441 DWORD dwErrCode = 0;
442 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
443 /* First, unmap the file view */
444 UnmapViewOfFile(self->data);
445 self->data = NULL;
446 /* Close the mapping object */
447 CloseHandle(self->map_handle);
448 self->map_handle = NULL;
449 /* Move to the desired EOF position */
450 #if SIZEOF_SIZE_T > 4
451 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
452 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
453 off_hi = (DWORD)(self->offset >> 32);
454 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
455 #else
456 newSizeHigh = 0;
457 newSizeLow = (DWORD)(self->offset + new_size);
458 off_hi = 0;
459 off_lo = (DWORD)self->offset;
460 #endif
461 SetFilePointer(self->file_handle,
462 newSizeLow, &newSizeHigh, FILE_BEGIN);
463 /* Change the size of the file */
464 SetEndOfFile(self->file_handle);
465 /* Create another mapping object and remap the file view */
466 self->map_handle = CreateFileMapping(
467 self->file_handle,
468 NULL,
469 PAGE_READWRITE,
472 self->tagname);
473 if (self->map_handle != NULL) {
474 self->data = (char *) MapViewOfFile(self->map_handle,
475 FILE_MAP_WRITE,
476 off_hi,
477 off_lo,
478 new_size);
479 if (self->data != NULL) {
480 self->size = new_size;
481 Py_INCREF(Py_None);
482 return Py_None;
483 } else {
484 dwErrCode = GetLastError();
485 CloseHandle(self->map_handle);
486 self->map_handle = NULL;
488 } else {
489 dwErrCode = GetLastError();
491 PyErr_SetFromWindowsErr(dwErrCode);
492 return NULL;
493 #endif /* MS_WINDOWS */
495 #ifdef UNIX
496 #ifndef HAVE_MREMAP
497 } else {
498 PyErr_SetString(PyExc_SystemError,
499 "mmap: resizing not available--no mremap()");
500 return NULL;
501 #else
502 } else {
503 void *newmap;
505 if (ftruncate(self->fd, self->offset + new_size) == -1) {
506 PyErr_SetFromErrno(mmap_module_error);
507 return NULL;
510 #ifdef MREMAP_MAYMOVE
511 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
512 #else
513 #if defined(__NetBSD__)
514 newmap = mremap(self->data, self->size, self->data, new_size, 0);
515 #else
516 newmap = mremap(self->data, self->size, new_size, 0);
517 #endif /* __NetBSD__ */
518 #endif
519 if (newmap == (void *)-1)
521 PyErr_SetFromErrno(mmap_module_error);
522 return NULL;
524 self->data = newmap;
525 self->size = new_size;
526 Py_INCREF(Py_None);
527 return Py_None;
528 #endif /* HAVE_MREMAP */
529 #endif /* UNIX */
533 static PyObject *
534 mmap_tell_method(mmap_object *self, PyObject *unused)
536 CHECK_VALID(NULL);
537 return PyInt_FromSize_t(self->pos);
540 static PyObject *
541 mmap_flush_method(mmap_object *self, PyObject *args)
543 Py_ssize_t offset = 0;
544 Py_ssize_t size = self->size;
545 CHECK_VALID(NULL);
546 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
547 return NULL;
548 if ((size_t)(offset + size) > self->size) {
549 PyErr_SetString(PyExc_ValueError, "flush values out of range");
550 return NULL;
552 #ifdef MS_WINDOWS
553 return PyInt_FromLong((long) FlushViewOfFile(self->data+offset, size));
554 #elif defined(UNIX)
555 /* XXX semantics of return value? */
556 /* XXX flags for msync? */
557 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
558 PyErr_SetFromErrno(mmap_module_error);
559 return NULL;
561 return PyInt_FromLong(0);
562 #else
563 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
564 return NULL;
565 #endif
568 static PyObject *
569 mmap_seek_method(mmap_object *self, PyObject *args)
571 Py_ssize_t dist;
572 int how=0;
573 CHECK_VALID(NULL);
574 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
575 return NULL;
576 else {
577 size_t where;
578 switch (how) {
579 case 0: /* relative to start */
580 if (dist < 0)
581 goto onoutofrange;
582 where = dist;
583 break;
584 case 1: /* relative to current position */
585 if ((Py_ssize_t)self->pos + dist < 0)
586 goto onoutofrange;
587 where = self->pos + dist;
588 break;
589 case 2: /* relative to end */
590 if ((Py_ssize_t)self->size + dist < 0)
591 goto onoutofrange;
592 where = self->size + dist;
593 break;
594 default:
595 PyErr_SetString(PyExc_ValueError, "unknown seek type");
596 return NULL;
598 if (where > self->size)
599 goto onoutofrange;
600 self->pos = where;
601 Py_INCREF(Py_None);
602 return Py_None;
605 onoutofrange:
606 PyErr_SetString(PyExc_ValueError, "seek out of range");
607 return NULL;
610 static PyObject *
611 mmap_move_method(mmap_object *self, PyObject *args)
613 unsigned long dest, src, cnt;
614 CHECK_VALID(NULL);
615 if (!PyArg_ParseTuple(args, "kkk:move", &dest, &src, &cnt) ||
616 !is_writeable(self)) {
617 return NULL;
618 } else {
619 /* bounds check the values */
620 if (cnt < 0 || (cnt + dest) < cnt || (cnt + src) < cnt ||
621 src < 0 || src > self->size || (src + cnt) > self->size ||
622 dest < 0 || dest > self->size || (dest + cnt) > self->size) {
623 PyErr_SetString(PyExc_ValueError,
624 "source, destination, or count out of range");
625 return NULL;
627 memmove(self->data+dest, self->data+src, cnt);
628 Py_INCREF(Py_None);
629 return Py_None;
633 static struct PyMethodDef mmap_object_methods[] = {
634 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
635 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
636 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
637 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
638 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
639 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
640 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
641 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
642 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
643 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
644 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
645 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
646 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
647 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
648 {NULL, NULL} /* sentinel */
651 /* Functions for treating an mmap'ed file as a buffer */
653 static Py_ssize_t
654 mmap_buffer_getreadbuf(mmap_object *self, Py_ssize_t index, const void **ptr)
656 CHECK_VALID(-1);
657 if (index != 0) {
658 PyErr_SetString(PyExc_SystemError,
659 "Accessing non-existent mmap segment");
660 return -1;
662 *ptr = self->data;
663 return self->size;
666 static Py_ssize_t
667 mmap_buffer_getwritebuf(mmap_object *self, Py_ssize_t index, const void **ptr)
669 CHECK_VALID(-1);
670 if (index != 0) {
671 PyErr_SetString(PyExc_SystemError,
672 "Accessing non-existent mmap segment");
673 return -1;
675 if (!is_writeable(self))
676 return -1;
677 *ptr = self->data;
678 return self->size;
681 static Py_ssize_t
682 mmap_buffer_getsegcount(mmap_object *self, Py_ssize_t *lenp)
684 CHECK_VALID(-1);
685 if (lenp)
686 *lenp = self->size;
687 return 1;
690 static Py_ssize_t
691 mmap_buffer_getcharbuffer(mmap_object *self, Py_ssize_t index, const void **ptr)
693 if (index != 0) {
694 PyErr_SetString(PyExc_SystemError,
695 "accessing non-existent buffer segment");
696 return -1;
698 *ptr = (const char *)self->data;
699 return self->size;
702 static Py_ssize_t
703 mmap_length(mmap_object *self)
705 CHECK_VALID(-1);
706 return self->size;
709 static PyObject *
710 mmap_item(mmap_object *self, Py_ssize_t i)
712 CHECK_VALID(NULL);
713 if (i < 0 || (size_t)i >= self->size) {
714 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
715 return NULL;
717 return PyString_FromStringAndSize(self->data + i, 1);
720 static PyObject *
721 mmap_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh)
723 CHECK_VALID(NULL);
724 if (ilow < 0)
725 ilow = 0;
726 else if ((size_t)ilow > self->size)
727 ilow = self->size;
728 if (ihigh < 0)
729 ihigh = 0;
730 if (ihigh < ilow)
731 ihigh = ilow;
732 else if ((size_t)ihigh > self->size)
733 ihigh = self->size;
735 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
738 static PyObject *
739 mmap_subscript(mmap_object *self, PyObject *item)
741 CHECK_VALID(NULL);
742 if (PyIndex_Check(item)) {
743 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
744 if (i == -1 && PyErr_Occurred())
745 return NULL;
746 if (i < 0)
747 i += self->size;
748 if (i < 0 || (size_t)i >= self->size) {
749 PyErr_SetString(PyExc_IndexError,
750 "mmap index out of range");
751 return NULL;
753 return PyString_FromStringAndSize(self->data + i, 1);
755 else if (PySlice_Check(item)) {
756 Py_ssize_t start, stop, step, slicelen;
758 if (PySlice_GetIndicesEx((PySliceObject *)item, self->size,
759 &start, &stop, &step, &slicelen) < 0) {
760 return NULL;
763 if (slicelen <= 0)
764 return PyString_FromStringAndSize("", 0);
765 else if (step == 1)
766 return PyString_FromStringAndSize(self->data + start,
767 slicelen);
768 else {
769 char *result_buf = (char *)PyMem_Malloc(slicelen);
770 Py_ssize_t cur, i;
771 PyObject *result;
773 if (result_buf == NULL)
774 return PyErr_NoMemory();
775 for (cur = start, i = 0; i < slicelen;
776 cur += step, i++) {
777 result_buf[i] = self->data[cur];
779 result = PyString_FromStringAndSize(result_buf,
780 slicelen);
781 PyMem_Free(result_buf);
782 return result;
785 else {
786 PyErr_SetString(PyExc_TypeError,
787 "mmap indices must be integers");
788 return NULL;
792 static PyObject *
793 mmap_concat(mmap_object *self, PyObject *bb)
795 CHECK_VALID(NULL);
796 PyErr_SetString(PyExc_SystemError,
797 "mmaps don't support concatenation");
798 return NULL;
801 static PyObject *
802 mmap_repeat(mmap_object *self, Py_ssize_t n)
804 CHECK_VALID(NULL);
805 PyErr_SetString(PyExc_SystemError,
806 "mmaps don't support repeat operation");
807 return NULL;
810 static int
811 mmap_ass_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
813 const char *buf;
815 CHECK_VALID(-1);
816 if (ilow < 0)
817 ilow = 0;
818 else if ((size_t)ilow > self->size)
819 ilow = self->size;
820 if (ihigh < 0)
821 ihigh = 0;
822 if (ihigh < ilow)
823 ihigh = ilow;
824 else if ((size_t)ihigh > self->size)
825 ihigh = self->size;
827 if (v == NULL) {
828 PyErr_SetString(PyExc_TypeError,
829 "mmap object doesn't support slice deletion");
830 return -1;
832 if (! (PyString_Check(v)) ) {
833 PyErr_SetString(PyExc_IndexError,
834 "mmap slice assignment must be a string");
835 return -1;
837 if (PyString_Size(v) != (ihigh - ilow)) {
838 PyErr_SetString(PyExc_IndexError,
839 "mmap slice assignment is wrong size");
840 return -1;
842 if (!is_writeable(self))
843 return -1;
844 buf = PyString_AsString(v);
845 memcpy(self->data + ilow, buf, ihigh-ilow);
846 return 0;
849 static int
850 mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
852 const char *buf;
854 CHECK_VALID(-1);
855 if (i < 0 || (size_t)i >= self->size) {
856 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
857 return -1;
859 if (v == NULL) {
860 PyErr_SetString(PyExc_TypeError,
861 "mmap object doesn't support item deletion");
862 return -1;
864 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
865 PyErr_SetString(PyExc_IndexError,
866 "mmap assignment must be single-character string");
867 return -1;
869 if (!is_writeable(self))
870 return -1;
871 buf = PyString_AsString(v);
872 self->data[i] = buf[0];
873 return 0;
876 static int
877 mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
879 CHECK_VALID(-1);
881 if (PyIndex_Check(item)) {
882 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
883 const char *buf;
885 if (i == -1 && PyErr_Occurred())
886 return -1;
887 if (i < 0)
888 i += self->size;
889 if (i < 0 || (size_t)i >= self->size) {
890 PyErr_SetString(PyExc_IndexError,
891 "mmap index out of range");
892 return -1;
894 if (value == NULL) {
895 PyErr_SetString(PyExc_TypeError,
896 "mmap object doesn't support item deletion");
897 return -1;
899 if (!PyString_Check(value) || PyString_Size(value) != 1) {
900 PyErr_SetString(PyExc_IndexError,
901 "mmap assignment must be single-character string");
902 return -1;
904 if (!is_writeable(self))
905 return -1;
906 buf = PyString_AsString(value);
907 self->data[i] = buf[0];
908 return 0;
910 else if (PySlice_Check(item)) {
911 Py_ssize_t start, stop, step, slicelen;
913 if (PySlice_GetIndicesEx((PySliceObject *)item,
914 self->size, &start, &stop,
915 &step, &slicelen) < 0) {
916 return -1;
918 if (value == NULL) {
919 PyErr_SetString(PyExc_TypeError,
920 "mmap object doesn't support slice deletion");
921 return -1;
923 if (!PyString_Check(value)) {
924 PyErr_SetString(PyExc_IndexError,
925 "mmap slice assignment must be a string");
926 return -1;
928 if (PyString_Size(value) != slicelen) {
929 PyErr_SetString(PyExc_IndexError,
930 "mmap slice assignment is wrong size");
931 return -1;
933 if (!is_writeable(self))
934 return -1;
936 if (slicelen == 0)
937 return 0;
938 else if (step == 1) {
939 const char *buf = PyString_AsString(value);
941 if (buf == NULL)
942 return -1;
943 memcpy(self->data + start, buf, slicelen);
944 return 0;
946 else {
947 Py_ssize_t cur, i;
948 const char *buf = PyString_AsString(value);
950 if (buf == NULL)
951 return -1;
952 for (cur = start, i = 0; i < slicelen;
953 cur += step, i++) {
954 self->data[cur] = buf[i];
956 return 0;
959 else {
960 PyErr_SetString(PyExc_TypeError,
961 "mmap indices must be integer");
962 return -1;
966 static PySequenceMethods mmap_as_sequence = {
967 (lenfunc)mmap_length, /*sq_length*/
968 (binaryfunc)mmap_concat, /*sq_concat*/
969 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
970 (ssizeargfunc)mmap_item, /*sq_item*/
971 (ssizessizeargfunc)mmap_slice, /*sq_slice*/
972 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
973 (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/
976 static PyMappingMethods mmap_as_mapping = {
977 (lenfunc)mmap_length,
978 (binaryfunc)mmap_subscript,
979 (objobjargproc)mmap_ass_subscript,
982 static PyBufferProcs mmap_as_buffer = {
983 (readbufferproc)mmap_buffer_getreadbuf,
984 (writebufferproc)mmap_buffer_getwritebuf,
985 (segcountproc)mmap_buffer_getsegcount,
986 (charbufferproc)mmap_buffer_getcharbuffer,
989 static PyObject *
990 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
992 PyDoc_STRVAR(mmap_doc,
993 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
995 Maps length bytes from the file specified by the file handle fileno,\n\
996 and returns a mmap object. If length is larger than the current size\n\
997 of the file, the file is extended to contain length bytes. If length\n\
998 is 0, the maximum length of the map is the current size of the file,\n\
999 except that if the file is empty Windows raises an exception (you cannot\n\
1000 create an empty mapping on Windows).\n\
1002 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1004 Maps length bytes from the file specified by the file descriptor fileno,\n\
1005 and returns a mmap object. If length is 0, the maximum length of the map\n\
1006 will be the current size of the file when mmap is called.\n\
1007 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1008 private copy-on-write mapping, so changes to the contents of the mmap\n\
1009 object will be private to this process, and MAP_SHARED creates a mapping\n\
1010 that's shared with all other processes mapping the same areas of the file.\n\
1011 The default value is MAP_SHARED.\n\
1013 To map anonymous memory, pass -1 as the fileno (both versions).");
1016 static PyTypeObject mmap_object_type = {
1017 PyVarObject_HEAD_INIT(NULL, 0)
1018 "mmap.mmap", /* tp_name */
1019 sizeof(mmap_object), /* tp_size */
1020 0, /* tp_itemsize */
1021 /* methods */
1022 (destructor) mmap_object_dealloc, /* tp_dealloc */
1023 0, /* tp_print */
1024 0, /* tp_getattr */
1025 0, /* tp_setattr */
1026 0, /* tp_compare */
1027 0, /* tp_repr */
1028 0, /* tp_as_number */
1029 &mmap_as_sequence, /*tp_as_sequence*/
1030 &mmap_as_mapping, /*tp_as_mapping*/
1031 0, /*tp_hash*/
1032 0, /*tp_call*/
1033 0, /*tp_str*/
1034 PyObject_GenericGetAttr, /*tp_getattro*/
1035 0, /*tp_setattro*/
1036 &mmap_as_buffer, /*tp_as_buffer*/
1037 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
1038 mmap_doc, /*tp_doc*/
1039 0, /* tp_traverse */
1040 0, /* tp_clear */
1041 0, /* tp_richcompare */
1042 0, /* tp_weaklistoffset */
1043 0, /* tp_iter */
1044 0, /* tp_iternext */
1045 mmap_object_methods, /* tp_methods */
1046 0, /* tp_members */
1047 0, /* tp_getset */
1048 0, /* tp_base */
1049 0, /* tp_dict */
1050 0, /* tp_descr_get */
1051 0, /* tp_descr_set */
1052 0, /* tp_dictoffset */
1053 0, /* tp_init */
1054 PyType_GenericAlloc, /* tp_alloc */
1055 new_mmap_object, /* tp_new */
1056 PyObject_Del, /* tp_free */
1060 /* extract the map size from the given PyObject
1062 Returns -1 on error, with an appropriate Python exception raised. On
1063 success, the map size is returned. */
1064 static Py_ssize_t
1065 _GetMapSize(PyObject *o, const char* param)
1067 if (o == NULL)
1068 return 0;
1069 if (PyIndex_Check(o)) {
1070 Py_ssize_t i = PyNumber_AsSsize_t(o, PyExc_OverflowError);
1071 if (i==-1 && PyErr_Occurred())
1072 return -1;
1073 if (i < 0) {
1074 PyErr_Format(PyExc_OverflowError,
1075 "memory mapped %s must be positive",
1076 param);
1077 return -1;
1079 return i;
1082 PyErr_SetString(PyExc_TypeError, "map size must be an integral value");
1083 return -1;
1086 #ifdef UNIX
1087 static PyObject *
1088 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1090 #ifdef HAVE_FSTAT
1091 struct stat st;
1092 #endif
1093 mmap_object *m_obj;
1094 PyObject *map_size_obj = NULL, *offset_obj = NULL;
1095 Py_ssize_t map_size, offset;
1096 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1097 int devzero = -1;
1098 int access = (int)ACCESS_DEFAULT;
1099 static char *keywords[] = {"fileno", "length",
1100 "flags", "prot",
1101 "access", "offset", NULL};
1103 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iiiO", keywords,
1104 &fd, &map_size_obj, &flags, &prot,
1105 &access, &offset_obj))
1106 return NULL;
1107 map_size = _GetMapSize(map_size_obj, "size");
1108 if (map_size < 0)
1109 return NULL;
1110 offset = _GetMapSize(offset_obj, "offset");
1111 if (offset < 0)
1112 return NULL;
1114 if ((access != (int)ACCESS_DEFAULT) &&
1115 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1116 return PyErr_Format(PyExc_ValueError,
1117 "mmap can't specify both access and flags, prot.");
1118 switch ((access_mode)access) {
1119 case ACCESS_READ:
1120 flags = MAP_SHARED;
1121 prot = PROT_READ;
1122 break;
1123 case ACCESS_WRITE:
1124 flags = MAP_SHARED;
1125 prot = PROT_READ | PROT_WRITE;
1126 break;
1127 case ACCESS_COPY:
1128 flags = MAP_PRIVATE;
1129 prot = PROT_READ | PROT_WRITE;
1130 break;
1131 case ACCESS_DEFAULT:
1132 /* use the specified or default values of flags and prot */
1133 break;
1134 default:
1135 return PyErr_Format(PyExc_ValueError,
1136 "mmap invalid access parameter.");
1139 if (prot == PROT_READ) {
1140 access = ACCESS_READ;
1143 #ifdef HAVE_FSTAT
1144 # ifdef __VMS
1145 /* on OpenVMS we must ensure that all bytes are written to the file */
1146 if (fd != -1) {
1147 fsync(fd);
1149 # endif
1150 if (fd != -1 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
1151 if (map_size == 0) {
1152 map_size = st.st_size;
1153 } else if ((size_t)offset + (size_t)map_size > st.st_size) {
1154 PyErr_SetString(PyExc_ValueError,
1155 "mmap length is greater than file size");
1156 return NULL;
1159 #endif
1160 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1161 if (m_obj == NULL) {return NULL;}
1162 m_obj->data = NULL;
1163 m_obj->size = (size_t) map_size;
1164 m_obj->pos = (size_t) 0;
1165 m_obj->offset = offset;
1166 if (fd == -1) {
1167 m_obj->fd = -1;
1168 /* Assume the caller wants to map anonymous memory.
1169 This is the same behaviour as Windows. mmap.mmap(-1, size)
1170 on both Windows and Unix map anonymous memory.
1172 #ifdef MAP_ANONYMOUS
1173 /* BSD way to map anonymous memory */
1174 flags |= MAP_ANONYMOUS;
1175 #else
1176 /* SVR4 method to map anonymous memory is to open /dev/zero */
1177 fd = devzero = open("/dev/zero", O_RDWR);
1178 if (devzero == -1) {
1179 Py_DECREF(m_obj);
1180 PyErr_SetFromErrno(mmap_module_error);
1181 return NULL;
1183 #endif
1184 } else {
1185 m_obj->fd = dup(fd);
1186 if (m_obj->fd == -1) {
1187 Py_DECREF(m_obj);
1188 PyErr_SetFromErrno(mmap_module_error);
1189 return NULL;
1193 m_obj->data = mmap(NULL, map_size,
1194 prot, flags,
1195 fd, offset);
1197 if (devzero != -1) {
1198 close(devzero);
1201 if (m_obj->data == (char *)-1) {
1202 m_obj->data = NULL;
1203 Py_DECREF(m_obj);
1204 PyErr_SetFromErrno(mmap_module_error);
1205 return NULL;
1207 m_obj->access = (access_mode)access;
1208 return (PyObject *)m_obj;
1210 #endif /* UNIX */
1212 #ifdef MS_WINDOWS
1213 static PyObject *
1214 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1216 mmap_object *m_obj;
1217 PyObject *map_size_obj = NULL, *offset_obj = NULL;
1218 Py_ssize_t map_size, offset;
1219 DWORD off_hi; /* upper 32 bits of offset */
1220 DWORD off_lo; /* lower 32 bits of offset */
1221 DWORD size_hi; /* upper 32 bits of size */
1222 DWORD size_lo; /* lower 32 bits of size */
1223 char *tagname = "";
1224 DWORD dwErr = 0;
1225 int fileno;
1226 HANDLE fh = 0;
1227 int access = (access_mode)ACCESS_DEFAULT;
1228 DWORD flProtect, dwDesiredAccess;
1229 static char *keywords[] = { "fileno", "length",
1230 "tagname",
1231 "access", "offset", NULL };
1233 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|ziO", keywords,
1234 &fileno, &map_size_obj,
1235 &tagname, &access, &offset_obj)) {
1236 return NULL;
1239 switch((access_mode)access) {
1240 case ACCESS_READ:
1241 flProtect = PAGE_READONLY;
1242 dwDesiredAccess = FILE_MAP_READ;
1243 break;
1244 case ACCESS_DEFAULT: case ACCESS_WRITE:
1245 flProtect = PAGE_READWRITE;
1246 dwDesiredAccess = FILE_MAP_WRITE;
1247 break;
1248 case ACCESS_COPY:
1249 flProtect = PAGE_WRITECOPY;
1250 dwDesiredAccess = FILE_MAP_COPY;
1251 break;
1252 default:
1253 return PyErr_Format(PyExc_ValueError,
1254 "mmap invalid access parameter.");
1257 map_size = _GetMapSize(map_size_obj, "size");
1258 if (map_size < 0)
1259 return NULL;
1260 offset = _GetMapSize(offset_obj, "offset");
1261 if (offset < 0)
1262 return NULL;
1264 /* assume -1 and 0 both mean invalid filedescriptor
1265 to 'anonymously' map memory.
1266 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1267 XXX: Should this code be added?
1268 if (fileno == 0)
1269 PyErr_Warn(PyExc_DeprecationWarning,
1270 "don't use 0 for anonymous memory");
1272 if (fileno != -1 && fileno != 0) {
1273 fh = (HANDLE)_get_osfhandle(fileno);
1274 if (fh==(HANDLE)-1) {
1275 PyErr_SetFromErrno(mmap_module_error);
1276 return NULL;
1278 /* Win9x appears to need us seeked to zero */
1279 lseek(fileno, 0, SEEK_SET);
1282 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1283 if (m_obj == NULL)
1284 return NULL;
1285 /* Set every field to an invalid marker, so we can safely
1286 destruct the object in the face of failure */
1287 m_obj->data = NULL;
1288 m_obj->file_handle = INVALID_HANDLE_VALUE;
1289 m_obj->map_handle = NULL;
1290 m_obj->tagname = NULL;
1291 m_obj->offset = offset;
1293 if (fh) {
1294 /* It is necessary to duplicate the handle, so the
1295 Python code can close it on us */
1296 if (!DuplicateHandle(
1297 GetCurrentProcess(), /* source process handle */
1298 fh, /* handle to be duplicated */
1299 GetCurrentProcess(), /* target proc handle */
1300 (LPHANDLE)&m_obj->file_handle, /* result */
1301 0, /* access - ignored due to options value */
1302 FALSE, /* inherited by child processes? */
1303 DUPLICATE_SAME_ACCESS)) { /* options */
1304 dwErr = GetLastError();
1305 Py_DECREF(m_obj);
1306 PyErr_SetFromWindowsErr(dwErr);
1307 return NULL;
1309 if (!map_size) {
1310 DWORD low,high;
1311 low = GetFileSize(fh, &high);
1312 /* low might just happen to have the value INVALID_FILE_SIZE;
1313 so we need to check the last error also. */
1314 if (low == INVALID_FILE_SIZE &&
1315 (dwErr = GetLastError()) != NO_ERROR) {
1316 Py_DECREF(m_obj);
1317 return PyErr_SetFromWindowsErr(dwErr);
1320 #if SIZEOF_SIZE_T > 4
1321 m_obj->size = (((size_t)high)<<32) + low;
1322 #else
1323 if (high)
1324 /* File is too large to map completely */
1325 m_obj->size = (size_t)-1;
1326 else
1327 m_obj->size = low;
1328 #endif
1329 } else {
1330 m_obj->size = map_size;
1333 else {
1334 m_obj->size = map_size;
1337 /* set the initial position */
1338 m_obj->pos = (size_t) 0;
1340 /* set the tag name */
1341 if (tagname != NULL && *tagname != '\0') {
1342 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1343 if (m_obj->tagname == NULL) {
1344 PyErr_NoMemory();
1345 Py_DECREF(m_obj);
1346 return NULL;
1348 strcpy(m_obj->tagname, tagname);
1350 else
1351 m_obj->tagname = NULL;
1353 m_obj->access = (access_mode)access;
1354 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1355 * more than 4 bytes, we need to break it apart. Else (size_t
1356 * consumes 4 bytes), C doesn't define what happens if we shift
1357 * right by 32, so we need different code.
1359 #if SIZEOF_SIZE_T > 4
1360 size_hi = (DWORD)((offset + m_obj->size) >> 32);
1361 size_lo = (DWORD)((offset + m_obj->size) & 0xFFFFFFFF);
1362 off_hi = (DWORD)(offset >> 32);
1363 off_lo = (DWORD)(offset & 0xFFFFFFFF);
1364 #else
1365 size_hi = 0;
1366 size_lo = (DWORD)(offset + m_obj->size);
1367 off_hi = 0;
1368 off_lo = (DWORD)offset;
1369 #endif
1370 /* For files, it would be sufficient to pass 0 as size.
1371 For anonymous maps, we have to pass the size explicitly. */
1372 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1373 NULL,
1374 flProtect,
1375 size_hi,
1376 size_lo,
1377 m_obj->tagname);
1378 if (m_obj->map_handle != NULL) {
1379 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1380 dwDesiredAccess,
1381 off_hi,
1382 off_lo,
1383 m_obj->size);
1384 if (m_obj->data != NULL)
1385 return (PyObject *)m_obj;
1386 else {
1387 dwErr = GetLastError();
1388 CloseHandle(m_obj->map_handle);
1389 m_obj->map_handle = NULL;
1391 } else
1392 dwErr = GetLastError();
1393 Py_DECREF(m_obj);
1394 PyErr_SetFromWindowsErr(dwErr);
1395 return NULL;
1397 #endif /* MS_WINDOWS */
1399 static void
1400 setint(PyObject *d, const char *name, long value)
1402 PyObject *o = PyInt_FromLong(value);
1403 if (o && PyDict_SetItemString(d, name, o) == 0) {
1404 Py_DECREF(o);
1408 PyMODINIT_FUNC
1409 initmmap(void)
1411 PyObject *dict, *module;
1413 if (PyType_Ready(&mmap_object_type) < 0)
1414 return;
1416 module = Py_InitModule("mmap", NULL);
1417 if (module == NULL)
1418 return;
1419 dict = PyModule_GetDict(module);
1420 if (!dict)
1421 return;
1422 mmap_module_error = PyErr_NewException("mmap.error",
1423 PyExc_EnvironmentError , NULL);
1424 if (mmap_module_error == NULL)
1425 return;
1426 PyDict_SetItemString(dict, "error", mmap_module_error);
1427 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
1428 #ifdef PROT_EXEC
1429 setint(dict, "PROT_EXEC", PROT_EXEC);
1430 #endif
1431 #ifdef PROT_READ
1432 setint(dict, "PROT_READ", PROT_READ);
1433 #endif
1434 #ifdef PROT_WRITE
1435 setint(dict, "PROT_WRITE", PROT_WRITE);
1436 #endif
1438 #ifdef MAP_SHARED
1439 setint(dict, "MAP_SHARED", MAP_SHARED);
1440 #endif
1441 #ifdef MAP_PRIVATE
1442 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
1443 #endif
1444 #ifdef MAP_DENYWRITE
1445 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
1446 #endif
1447 #ifdef MAP_EXECUTABLE
1448 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
1449 #endif
1450 #ifdef MAP_ANONYMOUS
1451 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1452 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
1453 #endif
1455 setint(dict, "PAGESIZE", (long)my_getpagesize());
1457 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1459 setint(dict, "ACCESS_READ", ACCESS_READ);
1460 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1461 setint(dict, "ACCESS_COPY", ACCESS_COPY);