Issue #5768: Change to Unicode output logic and test case for same.
[python.git] / Modules / mmapmodule.c
blobf660d6bb0ba2f138b1a647543648e2a1dc5b9659
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 (void) close(self->fd);
162 self->fd = -1;
163 if (self->data != NULL) {
164 munmap(self->data, self->size);
165 self->data = NULL;
167 #endif
169 Py_INCREF(Py_None);
170 return Py_None;
173 #ifdef MS_WINDOWS
174 #define CHECK_VALID(err) \
175 do { \
176 if (self->map_handle == NULL) { \
177 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
178 return err; \
180 } while (0)
181 #endif /* MS_WINDOWS */
183 #ifdef UNIX
184 #define CHECK_VALID(err) \
185 do { \
186 if (self->data == NULL) { \
187 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
188 return err; \
190 } while (0)
191 #endif /* UNIX */
193 static PyObject *
194 mmap_read_byte_method(mmap_object *self,
195 PyObject *unused)
197 CHECK_VALID(NULL);
198 if (self->pos < self->size) {
199 char value = self->data[self->pos];
200 self->pos += 1;
201 return Py_BuildValue("c", value);
202 } else {
203 PyErr_SetString(PyExc_ValueError, "read byte out of range");
204 return NULL;
208 static PyObject *
209 mmap_read_line_method(mmap_object *self,
210 PyObject *unused)
212 char *start = self->data+self->pos;
213 char *eof = self->data+self->size;
214 char *eol;
215 PyObject *result;
217 CHECK_VALID(NULL);
219 eol = memchr(start, '\n', self->size - self->pos);
220 if (!eol)
221 eol = eof;
222 else
223 ++eol; /* we're interested in the position after the
224 newline. */
225 result = PyString_FromStringAndSize(start, (eol - start));
226 self->pos += (eol - start);
227 return result;
230 static PyObject *
231 mmap_read_method(mmap_object *self,
232 PyObject *args)
234 Py_ssize_t num_bytes;
235 PyObject *result;
237 CHECK_VALID(NULL);
238 if (!PyArg_ParseTuple(args, "n:read", &num_bytes))
239 return(NULL);
241 /* silently 'adjust' out-of-range requests */
242 if (num_bytes > self->size - self->pos) {
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;
247 return result;
250 static PyObject *
251 mmap_gfind(mmap_object *self,
252 PyObject *args,
253 int reverse)
255 Py_ssize_t start = self->pos;
256 Py_ssize_t end = self->size;
257 const char *needle;
258 Py_ssize_t len;
260 CHECK_VALID(NULL);
261 if (!PyArg_ParseTuple(args, reverse ? "s#|nn:rfind" : "s#|nn:find",
262 &needle, &len, &start, &end)) {
263 return NULL;
264 } else {
265 const char *p, *start_p, *end_p;
266 int sign = reverse ? -1 : 1;
268 if (start < 0)
269 start += self->size;
270 if (start < 0)
271 start = 0;
272 else if ((size_t)start > self->size)
273 start = self->size;
275 if (end < 0)
276 end += self->size;
277 if (end < 0)
278 end = 0;
279 else if ((size_t)end > self->size)
280 end = self->size;
282 start_p = self->data + start;
283 end_p = self->data + end;
285 for (p = (reverse ? end_p - len : start_p);
286 (p >= start_p) && (p + len <= end_p); p += sign) {
287 Py_ssize_t i;
288 for (i = 0; i < len && needle[i] == p[i]; ++i)
289 /* nothing */;
290 if (i == len) {
291 return PyInt_FromSsize_t(p - self->data);
294 return PyInt_FromLong(-1);
298 static PyObject *
299 mmap_find_method(mmap_object *self,
300 PyObject *args)
302 return mmap_gfind(self, args, 0);
305 static PyObject *
306 mmap_rfind_method(mmap_object *self,
307 PyObject *args)
309 return mmap_gfind(self, args, 1);
312 static int
313 is_writeable(mmap_object *self)
315 if (self->access != ACCESS_READ)
316 return 1;
317 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
318 return 0;
321 static int
322 is_resizeable(mmap_object *self)
324 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
325 return 1;
326 PyErr_Format(PyExc_TypeError,
327 "mmap can't resize a readonly or copy-on-write memory map.");
328 return 0;
332 static PyObject *
333 mmap_write_method(mmap_object *self,
334 PyObject *args)
336 Py_ssize_t length;
337 char *data;
339 CHECK_VALID(NULL);
340 if (!PyArg_ParseTuple(args, "s#:write", &data, &length))
341 return(NULL);
343 if (!is_writeable(self))
344 return NULL;
346 if ((self->pos + length) > self->size) {
347 PyErr_SetString(PyExc_ValueError, "data out of range");
348 return NULL;
350 memcpy(self->data+self->pos, data, length);
351 self->pos = self->pos+length;
352 Py_INCREF(Py_None);
353 return Py_None;
356 static PyObject *
357 mmap_write_byte_method(mmap_object *self,
358 PyObject *args)
360 char value;
362 CHECK_VALID(NULL);
363 if (!PyArg_ParseTuple(args, "c:write_byte", &value))
364 return(NULL);
366 if (!is_writeable(self))
367 return NULL;
369 if (self->pos < self->size) {
370 *(self->data+self->pos) = value;
371 self->pos += 1;
372 Py_INCREF(Py_None);
373 return Py_None;
375 else {
376 PyErr_SetString(PyExc_ValueError, "write byte out of range");
377 return NULL;
381 static PyObject *
382 mmap_size_method(mmap_object *self,
383 PyObject *unused)
385 CHECK_VALID(NULL);
387 #ifdef MS_WINDOWS
388 if (self->file_handle != INVALID_HANDLE_VALUE) {
389 DWORD low,high;
390 PY_LONG_LONG size;
391 low = GetFileSize(self->file_handle, &high);
392 if (low == INVALID_FILE_SIZE) {
393 /* It might be that the function appears to have failed,
394 when indeed its size equals INVALID_FILE_SIZE */
395 DWORD error = GetLastError();
396 if (error != NO_ERROR)
397 return PyErr_SetFromWindowsErr(error);
399 if (!high && low < LONG_MAX)
400 return PyInt_FromLong((long)low);
401 size = (((PY_LONG_LONG)high)<<32) + low;
402 return PyLong_FromLongLong(size);
403 } else {
404 return PyInt_FromSsize_t(self->size);
406 #endif /* MS_WINDOWS */
408 #ifdef UNIX
410 struct stat buf;
411 if (-1 == fstat(self->fd, &buf)) {
412 PyErr_SetFromErrno(mmap_module_error);
413 return NULL;
415 return PyInt_FromSsize_t(buf.st_size);
417 #endif /* UNIX */
420 /* This assumes that you want the entire file mapped,
421 / and when recreating the map will make the new file
422 / have the new size
424 / Is this really necessary? This could easily be done
425 / from python by just closing and re-opening with the
426 / new size?
429 static PyObject *
430 mmap_resize_method(mmap_object *self,
431 PyObject *args)
433 Py_ssize_t new_size;
434 CHECK_VALID(NULL);
435 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
436 !is_resizeable(self)) {
437 return NULL;
438 #ifdef MS_WINDOWS
439 } else {
440 DWORD dwErrCode = 0;
441 DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
442 /* First, unmap the file view */
443 UnmapViewOfFile(self->data);
444 self->data = NULL;
445 /* Close the mapping object */
446 CloseHandle(self->map_handle);
447 self->map_handle = NULL;
448 /* Move to the desired EOF position */
449 #if SIZEOF_SIZE_T > 4
450 newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
451 newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
452 off_hi = (DWORD)(self->offset >> 32);
453 off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
454 #else
455 newSizeHigh = 0;
456 newSizeLow = (DWORD)(self->offset + new_size);
457 off_hi = 0;
458 off_lo = (DWORD)self->offset;
459 #endif
460 SetFilePointer(self->file_handle,
461 newSizeLow, &newSizeHigh, FILE_BEGIN);
462 /* Change the size of the file */
463 SetEndOfFile(self->file_handle);
464 /* Create another mapping object and remap the file view */
465 self->map_handle = CreateFileMapping(
466 self->file_handle,
467 NULL,
468 PAGE_READWRITE,
471 self->tagname);
472 if (self->map_handle != NULL) {
473 self->data = (char *) MapViewOfFile(self->map_handle,
474 FILE_MAP_WRITE,
475 off_hi,
476 off_lo,
477 new_size);
478 if (self->data != NULL) {
479 self->size = new_size;
480 Py_INCREF(Py_None);
481 return Py_None;
482 } else {
483 dwErrCode = GetLastError();
484 CloseHandle(self->map_handle);
485 self->map_handle = NULL;
487 } else {
488 dwErrCode = GetLastError();
490 PyErr_SetFromWindowsErr(dwErrCode);
491 return NULL;
492 #endif /* MS_WINDOWS */
494 #ifdef UNIX
495 #ifndef HAVE_MREMAP
496 } else {
497 PyErr_SetString(PyExc_SystemError,
498 "mmap: resizing not available--no mremap()");
499 return NULL;
500 #else
501 } else {
502 void *newmap;
504 if (ftruncate(self->fd, self->offset + new_size) == -1) {
505 PyErr_SetFromErrno(mmap_module_error);
506 return NULL;
509 #ifdef MREMAP_MAYMOVE
510 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
511 #else
512 #if defined(__NetBSD__)
513 newmap = mremap(self->data, self->size, self->data, new_size, 0);
514 #else
515 newmap = mremap(self->data, self->size, new_size, 0);
516 #endif /* __NetBSD__ */
517 #endif
518 if (newmap == (void *)-1)
520 PyErr_SetFromErrno(mmap_module_error);
521 return NULL;
523 self->data = newmap;
524 self->size = new_size;
525 Py_INCREF(Py_None);
526 return Py_None;
527 #endif /* HAVE_MREMAP */
528 #endif /* UNIX */
532 static PyObject *
533 mmap_tell_method(mmap_object *self, PyObject *unused)
535 CHECK_VALID(NULL);
536 return PyInt_FromSize_t(self->pos);
539 static PyObject *
540 mmap_flush_method(mmap_object *self, PyObject *args)
542 Py_ssize_t offset = 0;
543 Py_ssize_t size = self->size;
544 CHECK_VALID(NULL);
545 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
546 return NULL;
547 if ((size_t)(offset + size) > self->size) {
548 PyErr_SetString(PyExc_ValueError, "flush values out of range");
549 return NULL;
551 #ifdef MS_WINDOWS
552 return PyInt_FromLong((long) FlushViewOfFile(self->data+offset, size));
553 #elif defined(UNIX)
554 /* XXX semantics of return value? */
555 /* XXX flags for msync? */
556 if (-1 == msync(self->data + offset, size, MS_SYNC)) {
557 PyErr_SetFromErrno(mmap_module_error);
558 return NULL;
560 return PyInt_FromLong(0);
561 #else
562 PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
563 return NULL;
564 #endif
567 static PyObject *
568 mmap_seek_method(mmap_object *self, PyObject *args)
570 Py_ssize_t dist;
571 int how=0;
572 CHECK_VALID(NULL);
573 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
574 return NULL;
575 else {
576 size_t where;
577 switch (how) {
578 case 0: /* relative to start */
579 if (dist < 0)
580 goto onoutofrange;
581 where = dist;
582 break;
583 case 1: /* relative to current position */
584 if ((Py_ssize_t)self->pos + dist < 0)
585 goto onoutofrange;
586 where = self->pos + dist;
587 break;
588 case 2: /* relative to end */
589 if ((Py_ssize_t)self->size + dist < 0)
590 goto onoutofrange;
591 where = self->size + dist;
592 break;
593 default:
594 PyErr_SetString(PyExc_ValueError, "unknown seek type");
595 return NULL;
597 if (where > self->size)
598 goto onoutofrange;
599 self->pos = where;
600 Py_INCREF(Py_None);
601 return Py_None;
604 onoutofrange:
605 PyErr_SetString(PyExc_ValueError, "seek out of range");
606 return NULL;
609 static PyObject *
610 mmap_move_method(mmap_object *self, PyObject *args)
612 unsigned long dest, src, cnt;
613 CHECK_VALID(NULL);
614 if (!PyArg_ParseTuple(args, "kkk:move", &dest, &src, &cnt) ||
615 !is_writeable(self)) {
616 return NULL;
617 } else {
618 /* bounds check the values */
619 if (cnt < 0 || (cnt + dest) < cnt || (cnt + src) < cnt ||
620 src < 0 || src > self->size || (src + cnt) > self->size ||
621 dest < 0 || dest > self->size || (dest + cnt) > self->size) {
622 PyErr_SetString(PyExc_ValueError,
623 "source, destination, or count out of range");
624 return NULL;
626 memmove(self->data+dest, self->data+src, cnt);
627 Py_INCREF(Py_None);
628 return Py_None;
632 static struct PyMethodDef mmap_object_methods[] = {
633 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
634 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
635 {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
636 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
637 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
638 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
639 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
640 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
641 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
642 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
643 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
644 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
645 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
646 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
647 {NULL, NULL} /* sentinel */
650 /* Functions for treating an mmap'ed file as a buffer */
652 static Py_ssize_t
653 mmap_buffer_getreadbuf(mmap_object *self, Py_ssize_t index, const void **ptr)
655 CHECK_VALID(-1);
656 if (index != 0) {
657 PyErr_SetString(PyExc_SystemError,
658 "Accessing non-existent mmap segment");
659 return -1;
661 *ptr = self->data;
662 return self->size;
665 static Py_ssize_t
666 mmap_buffer_getwritebuf(mmap_object *self, Py_ssize_t index, const void **ptr)
668 CHECK_VALID(-1);
669 if (index != 0) {
670 PyErr_SetString(PyExc_SystemError,
671 "Accessing non-existent mmap segment");
672 return -1;
674 if (!is_writeable(self))
675 return -1;
676 *ptr = self->data;
677 return self->size;
680 static Py_ssize_t
681 mmap_buffer_getsegcount(mmap_object *self, Py_ssize_t *lenp)
683 CHECK_VALID(-1);
684 if (lenp)
685 *lenp = self->size;
686 return 1;
689 static Py_ssize_t
690 mmap_buffer_getcharbuffer(mmap_object *self, Py_ssize_t index, const void **ptr)
692 if (index != 0) {
693 PyErr_SetString(PyExc_SystemError,
694 "accessing non-existent buffer segment");
695 return -1;
697 *ptr = (const char *)self->data;
698 return self->size;
701 static Py_ssize_t
702 mmap_length(mmap_object *self)
704 CHECK_VALID(-1);
705 return self->size;
708 static PyObject *
709 mmap_item(mmap_object *self, Py_ssize_t i)
711 CHECK_VALID(NULL);
712 if (i < 0 || (size_t)i >= self->size) {
713 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
714 return NULL;
716 return PyString_FromStringAndSize(self->data + i, 1);
719 static PyObject *
720 mmap_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh)
722 CHECK_VALID(NULL);
723 if (ilow < 0)
724 ilow = 0;
725 else if ((size_t)ilow > self->size)
726 ilow = self->size;
727 if (ihigh < 0)
728 ihigh = 0;
729 if (ihigh < ilow)
730 ihigh = ilow;
731 else if ((size_t)ihigh > self->size)
732 ihigh = self->size;
734 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
737 static PyObject *
738 mmap_subscript(mmap_object *self, PyObject *item)
740 CHECK_VALID(NULL);
741 if (PyIndex_Check(item)) {
742 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
743 if (i == -1 && PyErr_Occurred())
744 return NULL;
745 if (i < 0)
746 i += self->size;
747 if (i < 0 || (size_t)i >= self->size) {
748 PyErr_SetString(PyExc_IndexError,
749 "mmap index out of range");
750 return NULL;
752 return PyString_FromStringAndSize(self->data + i, 1);
754 else if (PySlice_Check(item)) {
755 Py_ssize_t start, stop, step, slicelen;
757 if (PySlice_GetIndicesEx((PySliceObject *)item, self->size,
758 &start, &stop, &step, &slicelen) < 0) {
759 return NULL;
762 if (slicelen <= 0)
763 return PyString_FromStringAndSize("", 0);
764 else if (step == 1)
765 return PyString_FromStringAndSize(self->data + start,
766 slicelen);
767 else {
768 char *result_buf = (char *)PyMem_Malloc(slicelen);
769 Py_ssize_t cur, i;
770 PyObject *result;
772 if (result_buf == NULL)
773 return PyErr_NoMemory();
774 for (cur = start, i = 0; i < slicelen;
775 cur += step, i++) {
776 result_buf[i] = self->data[cur];
778 result = PyString_FromStringAndSize(result_buf,
779 slicelen);
780 PyMem_Free(result_buf);
781 return result;
784 else {
785 PyErr_SetString(PyExc_TypeError,
786 "mmap indices must be integers");
787 return NULL;
791 static PyObject *
792 mmap_concat(mmap_object *self, PyObject *bb)
794 CHECK_VALID(NULL);
795 PyErr_SetString(PyExc_SystemError,
796 "mmaps don't support concatenation");
797 return NULL;
800 static PyObject *
801 mmap_repeat(mmap_object *self, Py_ssize_t n)
803 CHECK_VALID(NULL);
804 PyErr_SetString(PyExc_SystemError,
805 "mmaps don't support repeat operation");
806 return NULL;
809 static int
810 mmap_ass_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
812 const char *buf;
814 CHECK_VALID(-1);
815 if (ilow < 0)
816 ilow = 0;
817 else if ((size_t)ilow > self->size)
818 ilow = self->size;
819 if (ihigh < 0)
820 ihigh = 0;
821 if (ihigh < ilow)
822 ihigh = ilow;
823 else if ((size_t)ihigh > self->size)
824 ihigh = self->size;
826 if (v == NULL) {
827 PyErr_SetString(PyExc_TypeError,
828 "mmap object doesn't support slice deletion");
829 return -1;
831 if (! (PyString_Check(v)) ) {
832 PyErr_SetString(PyExc_IndexError,
833 "mmap slice assignment must be a string");
834 return -1;
836 if (PyString_Size(v) != (ihigh - ilow)) {
837 PyErr_SetString(PyExc_IndexError,
838 "mmap slice assignment is wrong size");
839 return -1;
841 if (!is_writeable(self))
842 return -1;
843 buf = PyString_AsString(v);
844 memcpy(self->data + ilow, buf, ihigh-ilow);
845 return 0;
848 static int
849 mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
851 const char *buf;
853 CHECK_VALID(-1);
854 if (i < 0 || (size_t)i >= self->size) {
855 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
856 return -1;
858 if (v == NULL) {
859 PyErr_SetString(PyExc_TypeError,
860 "mmap object doesn't support item deletion");
861 return -1;
863 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
864 PyErr_SetString(PyExc_IndexError,
865 "mmap assignment must be single-character string");
866 return -1;
868 if (!is_writeable(self))
869 return -1;
870 buf = PyString_AsString(v);
871 self->data[i] = buf[0];
872 return 0;
875 static int
876 mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
878 CHECK_VALID(-1);
880 if (PyIndex_Check(item)) {
881 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
882 const char *buf;
884 if (i == -1 && PyErr_Occurred())
885 return -1;
886 if (i < 0)
887 i += self->size;
888 if (i < 0 || (size_t)i >= self->size) {
889 PyErr_SetString(PyExc_IndexError,
890 "mmap index out of range");
891 return -1;
893 if (value == NULL) {
894 PyErr_SetString(PyExc_TypeError,
895 "mmap object doesn't support item deletion");
896 return -1;
898 if (!PyString_Check(value) || PyString_Size(value) != 1) {
899 PyErr_SetString(PyExc_IndexError,
900 "mmap assignment must be single-character string");
901 return -1;
903 if (!is_writeable(self))
904 return -1;
905 buf = PyString_AsString(value);
906 self->data[i] = buf[0];
907 return 0;
909 else if (PySlice_Check(item)) {
910 Py_ssize_t start, stop, step, slicelen;
912 if (PySlice_GetIndicesEx((PySliceObject *)item,
913 self->size, &start, &stop,
914 &step, &slicelen) < 0) {
915 return -1;
917 if (value == NULL) {
918 PyErr_SetString(PyExc_TypeError,
919 "mmap object doesn't support slice deletion");
920 return -1;
922 if (!PyString_Check(value)) {
923 PyErr_SetString(PyExc_IndexError,
924 "mmap slice assignment must be a string");
925 return -1;
927 if (PyString_Size(value) != slicelen) {
928 PyErr_SetString(PyExc_IndexError,
929 "mmap slice assignment is wrong size");
930 return -1;
932 if (!is_writeable(self))
933 return -1;
935 if (slicelen == 0)
936 return 0;
937 else if (step == 1) {
938 const char *buf = PyString_AsString(value);
940 if (buf == NULL)
941 return -1;
942 memcpy(self->data + start, buf, slicelen);
943 return 0;
945 else {
946 Py_ssize_t cur, i;
947 const char *buf = PyString_AsString(value);
949 if (buf == NULL)
950 return -1;
951 for (cur = start, i = 0; i < slicelen;
952 cur += step, i++) {
953 self->data[cur] = buf[i];
955 return 0;
958 else {
959 PyErr_SetString(PyExc_TypeError,
960 "mmap indices must be integer");
961 return -1;
965 static PySequenceMethods mmap_as_sequence = {
966 (lenfunc)mmap_length, /*sq_length*/
967 (binaryfunc)mmap_concat, /*sq_concat*/
968 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
969 (ssizeargfunc)mmap_item, /*sq_item*/
970 (ssizessizeargfunc)mmap_slice, /*sq_slice*/
971 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
972 (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/
975 static PyMappingMethods mmap_as_mapping = {
976 (lenfunc)mmap_length,
977 (binaryfunc)mmap_subscript,
978 (objobjargproc)mmap_ass_subscript,
981 static PyBufferProcs mmap_as_buffer = {
982 (readbufferproc)mmap_buffer_getreadbuf,
983 (writebufferproc)mmap_buffer_getwritebuf,
984 (segcountproc)mmap_buffer_getsegcount,
985 (charbufferproc)mmap_buffer_getcharbuffer,
988 static PyObject *
989 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
991 PyDoc_STRVAR(mmap_doc,
992 "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
994 Maps length bytes from the file specified by the file handle fileno,\n\
995 and returns a mmap object. If length is larger than the current size\n\
996 of the file, the file is extended to contain length bytes. If length\n\
997 is 0, the maximum length of the map is the current size of the file,\n\
998 except that if the file is empty Windows raises an exception (you cannot\n\
999 create an empty mapping on Windows).\n\
1001 Unix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1003 Maps length bytes from the file specified by the file descriptor fileno,\n\
1004 and returns a mmap object. If length is 0, the maximum length of the map\n\
1005 will be the current size of the file when mmap is called.\n\
1006 flags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1007 private copy-on-write mapping, so changes to the contents of the mmap\n\
1008 object will be private to this process, and MAP_SHARED creates a mapping\n\
1009 that's shared with all other processes mapping the same areas of the file.\n\
1010 The default value is MAP_SHARED.\n\
1012 To map anonymous memory, pass -1 as the fileno (both versions).");
1015 static PyTypeObject mmap_object_type = {
1016 PyVarObject_HEAD_INIT(NULL, 0)
1017 "mmap.mmap", /* tp_name */
1018 sizeof(mmap_object), /* tp_size */
1019 0, /* tp_itemsize */
1020 /* methods */
1021 (destructor) mmap_object_dealloc, /* tp_dealloc */
1022 0, /* tp_print */
1023 0, /* tp_getattr */
1024 0, /* tp_setattr */
1025 0, /* tp_compare */
1026 0, /* tp_repr */
1027 0, /* tp_as_number */
1028 &mmap_as_sequence, /*tp_as_sequence*/
1029 &mmap_as_mapping, /*tp_as_mapping*/
1030 0, /*tp_hash*/
1031 0, /*tp_call*/
1032 0, /*tp_str*/
1033 PyObject_GenericGetAttr, /*tp_getattro*/
1034 0, /*tp_setattro*/
1035 &mmap_as_buffer, /*tp_as_buffer*/
1036 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
1037 mmap_doc, /*tp_doc*/
1038 0, /* tp_traverse */
1039 0, /* tp_clear */
1040 0, /* tp_richcompare */
1041 0, /* tp_weaklistoffset */
1042 0, /* tp_iter */
1043 0, /* tp_iternext */
1044 mmap_object_methods, /* tp_methods */
1045 0, /* tp_members */
1046 0, /* tp_getset */
1047 0, /* tp_base */
1048 0, /* tp_dict */
1049 0, /* tp_descr_get */
1050 0, /* tp_descr_set */
1051 0, /* tp_dictoffset */
1052 0, /* tp_init */
1053 PyType_GenericAlloc, /* tp_alloc */
1054 new_mmap_object, /* tp_new */
1055 PyObject_Del, /* tp_free */
1059 /* extract the map size from the given PyObject
1061 Returns -1 on error, with an appropriate Python exception raised. On
1062 success, the map size is returned. */
1063 static Py_ssize_t
1064 _GetMapSize(PyObject *o, const char* param)
1066 if (o == NULL)
1067 return 0;
1068 if (PyIndex_Check(o)) {
1069 Py_ssize_t i = PyNumber_AsSsize_t(o, PyExc_OverflowError);
1070 if (i==-1 && PyErr_Occurred())
1071 return -1;
1072 if (i < 0) {
1073 PyErr_Format(PyExc_OverflowError,
1074 "memory mapped %s must be positive",
1075 param);
1076 return -1;
1078 return i;
1081 PyErr_SetString(PyExc_TypeError, "map size must be an integral value");
1082 return -1;
1085 #ifdef UNIX
1086 static PyObject *
1087 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1089 #ifdef HAVE_FSTAT
1090 struct stat st;
1091 #endif
1092 mmap_object *m_obj;
1093 PyObject *map_size_obj = NULL, *offset_obj = NULL;
1094 Py_ssize_t map_size, offset;
1095 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1096 int devzero = -1;
1097 int access = (int)ACCESS_DEFAULT;
1098 static char *keywords[] = {"fileno", "length",
1099 "flags", "prot",
1100 "access", "offset", NULL};
1102 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iiiO", keywords,
1103 &fd, &map_size_obj, &flags, &prot,
1104 &access, &offset_obj))
1105 return NULL;
1106 map_size = _GetMapSize(map_size_obj, "size");
1107 if (map_size < 0)
1108 return NULL;
1109 offset = _GetMapSize(offset_obj, "offset");
1110 if (offset < 0)
1111 return NULL;
1113 if ((access != (int)ACCESS_DEFAULT) &&
1114 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1115 return PyErr_Format(PyExc_ValueError,
1116 "mmap can't specify both access and flags, prot.");
1117 switch ((access_mode)access) {
1118 case ACCESS_READ:
1119 flags = MAP_SHARED;
1120 prot = PROT_READ;
1121 break;
1122 case ACCESS_WRITE:
1123 flags = MAP_SHARED;
1124 prot = PROT_READ | PROT_WRITE;
1125 break;
1126 case ACCESS_COPY:
1127 flags = MAP_PRIVATE;
1128 prot = PROT_READ | PROT_WRITE;
1129 break;
1130 case ACCESS_DEFAULT:
1131 /* use the specified or default values of flags and prot */
1132 break;
1133 default:
1134 return PyErr_Format(PyExc_ValueError,
1135 "mmap invalid access parameter.");
1138 if (prot == PROT_READ) {
1139 access = ACCESS_READ;
1142 #ifdef HAVE_FSTAT
1143 # ifdef __VMS
1144 /* on OpenVMS we must ensure that all bytes are written to the file */
1145 if (fd != -1) {
1146 fsync(fd);
1148 # endif
1149 if (fd != -1 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
1150 if (map_size == 0) {
1151 map_size = st.st_size;
1152 } else if ((size_t)offset + (size_t)map_size > st.st_size) {
1153 PyErr_SetString(PyExc_ValueError,
1154 "mmap length is greater than file size");
1155 return NULL;
1158 #endif
1159 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1160 if (m_obj == NULL) {return NULL;}
1161 m_obj->data = NULL;
1162 m_obj->size = (size_t) map_size;
1163 m_obj->pos = (size_t) 0;
1164 m_obj->offset = offset;
1165 if (fd == -1) {
1166 m_obj->fd = -1;
1167 /* Assume the caller wants to map anonymous memory.
1168 This is the same behaviour as Windows. mmap.mmap(-1, size)
1169 on both Windows and Unix map anonymous memory.
1171 #ifdef MAP_ANONYMOUS
1172 /* BSD way to map anonymous memory */
1173 flags |= MAP_ANONYMOUS;
1174 #else
1175 /* SVR4 method to map anonymous memory is to open /dev/zero */
1176 fd = devzero = open("/dev/zero", O_RDWR);
1177 if (devzero == -1) {
1178 Py_DECREF(m_obj);
1179 PyErr_SetFromErrno(mmap_module_error);
1180 return NULL;
1182 #endif
1183 } else {
1184 m_obj->fd = dup(fd);
1185 if (m_obj->fd == -1) {
1186 Py_DECREF(m_obj);
1187 PyErr_SetFromErrno(mmap_module_error);
1188 return NULL;
1192 m_obj->data = mmap(NULL, map_size,
1193 prot, flags,
1194 fd, offset);
1196 if (devzero != -1) {
1197 close(devzero);
1200 if (m_obj->data == (char *)-1) {
1201 m_obj->data = NULL;
1202 Py_DECREF(m_obj);
1203 PyErr_SetFromErrno(mmap_module_error);
1204 return NULL;
1206 m_obj->access = (access_mode)access;
1207 return (PyObject *)m_obj;
1209 #endif /* UNIX */
1211 #ifdef MS_WINDOWS
1212 static PyObject *
1213 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1215 mmap_object *m_obj;
1216 PyObject *map_size_obj = NULL, *offset_obj = NULL;
1217 Py_ssize_t map_size, offset;
1218 DWORD off_hi; /* upper 32 bits of offset */
1219 DWORD off_lo; /* lower 32 bits of offset */
1220 DWORD size_hi; /* upper 32 bits of size */
1221 DWORD size_lo; /* lower 32 bits of size */
1222 char *tagname = "";
1223 DWORD dwErr = 0;
1224 int fileno;
1225 HANDLE fh = 0;
1226 int access = (access_mode)ACCESS_DEFAULT;
1227 DWORD flProtect, dwDesiredAccess;
1228 static char *keywords[] = { "fileno", "length",
1229 "tagname",
1230 "access", "offset", NULL };
1232 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|ziO", keywords,
1233 &fileno, &map_size_obj,
1234 &tagname, &access, &offset_obj)) {
1235 return NULL;
1238 switch((access_mode)access) {
1239 case ACCESS_READ:
1240 flProtect = PAGE_READONLY;
1241 dwDesiredAccess = FILE_MAP_READ;
1242 break;
1243 case ACCESS_DEFAULT: case ACCESS_WRITE:
1244 flProtect = PAGE_READWRITE;
1245 dwDesiredAccess = FILE_MAP_WRITE;
1246 break;
1247 case ACCESS_COPY:
1248 flProtect = PAGE_WRITECOPY;
1249 dwDesiredAccess = FILE_MAP_COPY;
1250 break;
1251 default:
1252 return PyErr_Format(PyExc_ValueError,
1253 "mmap invalid access parameter.");
1256 map_size = _GetMapSize(map_size_obj, "size");
1257 if (map_size < 0)
1258 return NULL;
1259 offset = _GetMapSize(offset_obj, "offset");
1260 if (offset < 0)
1261 return NULL;
1263 /* assume -1 and 0 both mean invalid filedescriptor
1264 to 'anonymously' map memory.
1265 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1266 XXX: Should this code be added?
1267 if (fileno == 0)
1268 PyErr_Warn(PyExc_DeprecationWarning,
1269 "don't use 0 for anonymous memory");
1271 if (fileno != -1 && fileno != 0) {
1272 fh = (HANDLE)_get_osfhandle(fileno);
1273 if (fh==(HANDLE)-1) {
1274 PyErr_SetFromErrno(mmap_module_error);
1275 return NULL;
1277 /* Win9x appears to need us seeked to zero */
1278 lseek(fileno, 0, SEEK_SET);
1281 m_obj = (mmap_object *)type->tp_alloc(type, 0);
1282 if (m_obj == NULL)
1283 return NULL;
1284 /* Set every field to an invalid marker, so we can safely
1285 destruct the object in the face of failure */
1286 m_obj->data = NULL;
1287 m_obj->file_handle = INVALID_HANDLE_VALUE;
1288 m_obj->map_handle = NULL;
1289 m_obj->tagname = NULL;
1290 m_obj->offset = offset;
1292 if (fh) {
1293 /* It is necessary to duplicate the handle, so the
1294 Python code can close it on us */
1295 if (!DuplicateHandle(
1296 GetCurrentProcess(), /* source process handle */
1297 fh, /* handle to be duplicated */
1298 GetCurrentProcess(), /* target proc handle */
1299 (LPHANDLE)&m_obj->file_handle, /* result */
1300 0, /* access - ignored due to options value */
1301 FALSE, /* inherited by child processes? */
1302 DUPLICATE_SAME_ACCESS)) { /* options */
1303 dwErr = GetLastError();
1304 Py_DECREF(m_obj);
1305 PyErr_SetFromWindowsErr(dwErr);
1306 return NULL;
1308 if (!map_size) {
1309 DWORD low,high;
1310 low = GetFileSize(fh, &high);
1311 /* low might just happen to have the value INVALID_FILE_SIZE;
1312 so we need to check the last error also. */
1313 if (low == INVALID_FILE_SIZE &&
1314 (dwErr = GetLastError()) != NO_ERROR) {
1315 Py_DECREF(m_obj);
1316 return PyErr_SetFromWindowsErr(dwErr);
1319 #if SIZEOF_SIZE_T > 4
1320 m_obj->size = (((size_t)high)<<32) + low;
1321 #else
1322 if (high)
1323 /* File is too large to map completely */
1324 m_obj->size = (size_t)-1;
1325 else
1326 m_obj->size = low;
1327 #endif
1328 } else {
1329 m_obj->size = map_size;
1332 else {
1333 m_obj->size = map_size;
1336 /* set the initial position */
1337 m_obj->pos = (size_t) 0;
1339 /* set the tag name */
1340 if (tagname != NULL && *tagname != '\0') {
1341 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1342 if (m_obj->tagname == NULL) {
1343 PyErr_NoMemory();
1344 Py_DECREF(m_obj);
1345 return NULL;
1347 strcpy(m_obj->tagname, tagname);
1349 else
1350 m_obj->tagname = NULL;
1352 m_obj->access = (access_mode)access;
1353 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1354 * more than 4 bytes, we need to break it apart. Else (size_t
1355 * consumes 4 bytes), C doesn't define what happens if we shift
1356 * right by 32, so we need different code.
1358 #if SIZEOF_SIZE_T > 4
1359 size_hi = (DWORD)((offset + m_obj->size) >> 32);
1360 size_lo = (DWORD)((offset + m_obj->size) & 0xFFFFFFFF);
1361 off_hi = (DWORD)(offset >> 32);
1362 off_lo = (DWORD)(offset & 0xFFFFFFFF);
1363 #else
1364 size_hi = 0;
1365 size_lo = (DWORD)(offset + m_obj->size);
1366 off_hi = 0;
1367 off_lo = (DWORD)offset;
1368 #endif
1369 /* For files, it would be sufficient to pass 0 as size.
1370 For anonymous maps, we have to pass the size explicitly. */
1371 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1372 NULL,
1373 flProtect,
1374 size_hi,
1375 size_lo,
1376 m_obj->tagname);
1377 if (m_obj->map_handle != NULL) {
1378 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1379 dwDesiredAccess,
1380 off_hi,
1381 off_lo,
1382 m_obj->size);
1383 if (m_obj->data != NULL)
1384 return (PyObject *)m_obj;
1385 else {
1386 dwErr = GetLastError();
1387 CloseHandle(m_obj->map_handle);
1388 m_obj->map_handle = NULL;
1390 } else
1391 dwErr = GetLastError();
1392 Py_DECREF(m_obj);
1393 PyErr_SetFromWindowsErr(dwErr);
1394 return NULL;
1396 #endif /* MS_WINDOWS */
1398 static void
1399 setint(PyObject *d, const char *name, long value)
1401 PyObject *o = PyInt_FromLong(value);
1402 if (o && PyDict_SetItemString(d, name, o) == 0) {
1403 Py_DECREF(o);
1407 PyMODINIT_FUNC
1408 initmmap(void)
1410 PyObject *dict, *module;
1412 if (PyType_Ready(&mmap_object_type) < 0)
1413 return;
1415 module = Py_InitModule("mmap", NULL);
1416 if (module == NULL)
1417 return;
1418 dict = PyModule_GetDict(module);
1419 if (!dict)
1420 return;
1421 mmap_module_error = PyErr_NewException("mmap.error",
1422 PyExc_EnvironmentError , NULL);
1423 if (mmap_module_error == NULL)
1424 return;
1425 PyDict_SetItemString(dict, "error", mmap_module_error);
1426 PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
1427 #ifdef PROT_EXEC
1428 setint(dict, "PROT_EXEC", PROT_EXEC);
1429 #endif
1430 #ifdef PROT_READ
1431 setint(dict, "PROT_READ", PROT_READ);
1432 #endif
1433 #ifdef PROT_WRITE
1434 setint(dict, "PROT_WRITE", PROT_WRITE);
1435 #endif
1437 #ifdef MAP_SHARED
1438 setint(dict, "MAP_SHARED", MAP_SHARED);
1439 #endif
1440 #ifdef MAP_PRIVATE
1441 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
1442 #endif
1443 #ifdef MAP_DENYWRITE
1444 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
1445 #endif
1446 #ifdef MAP_EXECUTABLE
1447 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
1448 #endif
1449 #ifdef MAP_ANONYMOUS
1450 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1451 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
1452 #endif
1454 setint(dict, "PAGESIZE", (long)my_getpagesize());
1456 setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1458 setint(dict, "ACCESS_READ", ACCESS_READ);
1459 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1460 setint(dict, "ACCESS_COPY", ACCESS_COPY);