source: vendor/python/2.5/Modules/mmapmodule.c

Last change on this file was 3225, checked in by bird, 18 years ago

Python 2.5

File size: 27.7 KB
Line 
1/*
2 / Author: Sam Rushing <rushing@nightmare.com>
3 / Hacked for Unix by AMK
4 / $Id: mmapmodule.c 51474 2006-08-22 13:57:07Z neal.norwitz $
5
6 / mmapmodule.cpp -- map a view of a file into memory
7 /
8 / todo: need permission flags, perhaps a 'chsize' analog
9 / not all functions check range yet!!!
10 /
11 /
12 / This version of mmapmodule.c has been changed significantly
13 / from the original mmapfile.c on which it was based.
14 / The original version of mmapfile is maintained by Sam at
15 / ftp://squirl.nightmare.com/pub/python/python-ext.
16*/
17
18#define PY_SSIZE_T_CLEAN
19#include <Python.h>
20
21#ifndef MS_WINDOWS
22#define UNIX
23#endif
24
25#ifdef MS_WINDOWS
26#include <windows.h>
27static int
28my_getpagesize(void)
29{
30 SYSTEM_INFO si;
31 GetSystemInfo(&si);
32 return si.dwPageSize;
33}
34#endif
35
36#ifdef UNIX
37#include <sys/mman.h>
38#include <sys/stat.h>
39
40#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
41static int
42my_getpagesize(void)
43{
44 return sysconf(_SC_PAGESIZE);
45}
46#else
47#define my_getpagesize getpagesize
48#endif
49
50#endif /* UNIX */
51
52#include <string.h>
53
54#ifdef HAVE_SYS_TYPES_H
55#include <sys/types.h>
56#endif /* HAVE_SYS_TYPES_H */
57
58/* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
59#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
60# define MAP_ANONYMOUS MAP_ANON
61#endif
62
63static PyObject *mmap_module_error;
64
65typedef enum
66{
67 ACCESS_DEFAULT,
68 ACCESS_READ,
69 ACCESS_WRITE,
70 ACCESS_COPY
71} access_mode;
72
73typedef struct {
74 PyObject_HEAD
75 char * data;
76 size_t size;
77 size_t pos;
78
79#ifdef MS_WINDOWS
80 HANDLE map_handle;
81 HANDLE file_handle;
82 char * tagname;
83#endif
84
85#ifdef UNIX
86 int fd;
87#endif
88
89 access_mode access;
90} mmap_object;
91
92
93static void
94mmap_object_dealloc(mmap_object *m_obj)
95{
96#ifdef MS_WINDOWS
97 if (m_obj->data != NULL)
98 UnmapViewOfFile (m_obj->data);
99 if (m_obj->map_handle != INVALID_HANDLE_VALUE)
100 CloseHandle (m_obj->map_handle);
101 if (m_obj->file_handle != INVALID_HANDLE_VALUE)
102 CloseHandle (m_obj->file_handle);
103 if (m_obj->tagname)
104 PyMem_Free(m_obj->tagname);
105#endif /* MS_WINDOWS */
106
107#ifdef UNIX
108 if (m_obj->fd >= 0)
109 (void) close(m_obj->fd);
110 if (m_obj->data!=NULL) {
111 msync(m_obj->data, m_obj->size, MS_SYNC);
112 munmap(m_obj->data, m_obj->size);
113 }
114#endif /* UNIX */
115
116 PyObject_Del(m_obj);
117}
118
119static PyObject *
120mmap_close_method(mmap_object *self, PyObject *unused)
121{
122#ifdef MS_WINDOWS
123 /* For each resource we maintain, we need to check
124 the value is valid, and if so, free the resource
125 and set the member value to an invalid value so
126 the dealloc does not attempt to resource clearing
127 again.
128 TODO - should we check for errors in the close operations???
129 */
130 if (self->data != NULL) {
131 UnmapViewOfFile(self->data);
132 self->data = NULL;
133 }
134 if (self->map_handle != INVALID_HANDLE_VALUE) {
135 CloseHandle(self->map_handle);
136 self->map_handle = INVALID_HANDLE_VALUE;
137 }
138 if (self->file_handle != INVALID_HANDLE_VALUE) {
139 CloseHandle(self->file_handle);
140 self->file_handle = INVALID_HANDLE_VALUE;
141 }
142#endif /* MS_WINDOWS */
143
144#ifdef UNIX
145 (void) close(self->fd);
146 self->fd = -1;
147 if (self->data != NULL) {
148 munmap(self->data, self->size);
149 self->data = NULL;
150 }
151#endif
152
153 Py_INCREF(Py_None);
154 return Py_None;
155}
156
157#ifdef MS_WINDOWS
158#define CHECK_VALID(err) \
159do { \
160 if (self->map_handle == INVALID_HANDLE_VALUE) { \
161 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
162 return err; \
163 } \
164} while (0)
165#endif /* MS_WINDOWS */
166
167#ifdef UNIX
168#define CHECK_VALID(err) \
169do { \
170 if (self->data == NULL) { \
171 PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
172 return err; \
173 } \
174} while (0)
175#endif /* UNIX */
176
177static PyObject *
178mmap_read_byte_method(mmap_object *self,
179 PyObject *unused)
180{
181 CHECK_VALID(NULL);
182 if (self->pos < self->size) {
183 char value = self->data[self->pos];
184 self->pos += 1;
185 return Py_BuildValue("c", value);
186 } else {
187 PyErr_SetString(PyExc_ValueError, "read byte out of range");
188 return NULL;
189 }
190}
191
192static PyObject *
193mmap_read_line_method(mmap_object *self,
194 PyObject *unused)
195{
196 char *start = self->data+self->pos;
197 char *eof = self->data+self->size;
198 char *eol;
199 PyObject *result;
200
201 CHECK_VALID(NULL);
202
203 eol = memchr(start, '\n', self->size - self->pos);
204 if (!eol)
205 eol = eof;
206 else
207 ++eol; /* we're interested in the position after the
208 newline. */
209 result = PyString_FromStringAndSize(start, (eol - start));
210 self->pos += (eol - start);
211 return result;
212}
213
214static PyObject *
215mmap_read_method(mmap_object *self,
216 PyObject *args)
217{
218 Py_ssize_t num_bytes;
219 PyObject *result;
220
221 CHECK_VALID(NULL);
222 if (!PyArg_ParseTuple(args, "n:read", &num_bytes))
223 return(NULL);
224
225 /* silently 'adjust' out-of-range requests */
226 if ((self->pos + num_bytes) > self->size) {
227 num_bytes -= (self->pos+num_bytes) - self->size;
228 }
229 result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
230 self->pos += num_bytes;
231 return result;
232}
233
234static PyObject *
235mmap_find_method(mmap_object *self,
236 PyObject *args)
237{
238 Py_ssize_t start = self->pos;
239 char *needle;
240 Py_ssize_t len;
241
242 CHECK_VALID(NULL);
243 if (!PyArg_ParseTuple(args, "s#|n:find", &needle, &len, &start)) {
244 return NULL;
245 } else {
246 char *p;
247 char *e = self->data + self->size;
248
249 if (start < 0)
250 start += self->size;
251 if (start < 0)
252 start = 0;
253 else if ((size_t)start > self->size)
254 start = self->size;
255
256 for (p = self->data + start; p + len <= e; ++p) {
257 Py_ssize_t i;
258 for (i = 0; i < len && needle[i] == p[i]; ++i)
259 /* nothing */;
260 if (i == len) {
261 return PyInt_FromSsize_t(p - self->data);
262 }
263 }
264 return PyInt_FromLong(-1);
265 }
266}
267
268static int
269is_writeable(mmap_object *self)
270{
271 if (self->access != ACCESS_READ)
272 return 1;
273 PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
274 return 0;
275}
276
277static int
278is_resizeable(mmap_object *self)
279{
280 if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
281 return 1;
282 PyErr_Format(PyExc_TypeError,
283 "mmap can't resize a readonly or copy-on-write memory map.");
284 return 0;
285}
286
287
288static PyObject *
289mmap_write_method(mmap_object *self,
290 PyObject *args)
291{
292 Py_ssize_t length;
293 char *data;
294
295 CHECK_VALID(NULL);
296 if (!PyArg_ParseTuple(args, "s#:write", &data, &length))
297 return(NULL);
298
299 if (!is_writeable(self))
300 return NULL;
301
302 if ((self->pos + length) > self->size) {
303 PyErr_SetString(PyExc_ValueError, "data out of range");
304 return NULL;
305 }
306 memcpy(self->data+self->pos, data, length);
307 self->pos = self->pos+length;
308 Py_INCREF(Py_None);
309 return Py_None;
310}
311
312static PyObject *
313mmap_write_byte_method(mmap_object *self,
314 PyObject *args)
315{
316 char value;
317
318 CHECK_VALID(NULL);
319 if (!PyArg_ParseTuple(args, "c:write_byte", &value))
320 return(NULL);
321
322 if (!is_writeable(self))
323 return NULL;
324 *(self->data+self->pos) = value;
325 self->pos += 1;
326 Py_INCREF(Py_None);
327 return Py_None;
328}
329
330static PyObject *
331mmap_size_method(mmap_object *self,
332 PyObject *unused)
333{
334 CHECK_VALID(NULL);
335
336#ifdef MS_WINDOWS
337 if (self->file_handle != INVALID_HANDLE_VALUE) {
338 DWORD low,high;
339 PY_LONG_LONG size;
340 low = GetFileSize(self->file_handle, &high);
341 if (low == INVALID_FILE_SIZE) {
342 /* It might be that the function appears to have failed,
343 when indeed its size equals INVALID_FILE_SIZE */
344 DWORD error = GetLastError();
345 if (error != NO_ERROR)
346 return PyErr_SetFromWindowsErr(error);
347 }
348 if (!high && low < LONG_MAX)
349 return PyInt_FromLong((long)low);
350 size = (((PY_LONG_LONG)high)<<32) + low;
351 return PyLong_FromLongLong(size);
352 } else {
353 return PyInt_FromSsize_t(self->size);
354 }
355#endif /* MS_WINDOWS */
356
357#ifdef UNIX
358 {
359 struct stat buf;
360 if (-1 == fstat(self->fd, &buf)) {
361 PyErr_SetFromErrno(mmap_module_error);
362 return NULL;
363 }
364 return PyInt_FromSsize_t(buf.st_size);
365 }
366#endif /* UNIX */
367}
368
369/* This assumes that you want the entire file mapped,
370 / and when recreating the map will make the new file
371 / have the new size
372 /
373 / Is this really necessary? This could easily be done
374 / from python by just closing and re-opening with the
375 / new size?
376 */
377
378static PyObject *
379mmap_resize_method(mmap_object *self,
380 PyObject *args)
381{
382 Py_ssize_t new_size;
383 CHECK_VALID(NULL);
384 if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
385 !is_resizeable(self)) {
386 return NULL;
387#ifdef MS_WINDOWS
388 } else {
389 DWORD dwErrCode = 0;
390 DWORD newSizeLow, newSizeHigh;
391 /* First, unmap the file view */
392 UnmapViewOfFile(self->data);
393 /* Close the mapping object */
394 CloseHandle(self->map_handle);
395 /* Move to the desired EOF position */
396#if SIZEOF_SIZE_T > 4
397 newSizeHigh = (DWORD)(new_size >> 32);
398 newSizeLow = (DWORD)(new_size & 0xFFFFFFFF);
399#else
400 newSizeHigh = 0;
401 newSizeLow = (DWORD)new_size;
402#endif
403 SetFilePointer(self->file_handle,
404 newSizeLow, &newSizeHigh, FILE_BEGIN);
405 /* Change the size of the file */
406 SetEndOfFile(self->file_handle);
407 /* Create another mapping object and remap the file view */
408 self->map_handle = CreateFileMapping(
409 self->file_handle,
410 NULL,
411 PAGE_READWRITE,
412 newSizeHigh,
413 newSizeLow,
414 self->tagname);
415 if (self->map_handle != NULL) {
416 self->data = (char *) MapViewOfFile(self->map_handle,
417 FILE_MAP_WRITE,
418 0,
419 0,
420 0);
421 if (self->data != NULL) {
422 self->size = new_size;
423 Py_INCREF(Py_None);
424 return Py_None;
425 } else {
426 dwErrCode = GetLastError();
427 }
428 } else {
429 dwErrCode = GetLastError();
430 }
431 PyErr_SetFromWindowsErr(dwErrCode);
432 return NULL;
433#endif /* MS_WINDOWS */
434
435#ifdef UNIX
436#ifndef HAVE_MREMAP
437 } else {
438 PyErr_SetString(PyExc_SystemError,
439 "mmap: resizing not available--no mremap()");
440 return NULL;
441#else
442 } else {
443 void *newmap;
444
445 if (ftruncate(self->fd, new_size) == -1) {
446 PyErr_SetFromErrno(mmap_module_error);
447 return NULL;
448 }
449
450#ifdef MREMAP_MAYMOVE
451 newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
452#else
453 newmap = mremap(self->data, self->size, new_size, 0);
454#endif
455 if (newmap == (void *)-1)
456 {
457 PyErr_SetFromErrno(mmap_module_error);
458 return NULL;
459 }
460 self->data = newmap;
461 self->size = new_size;
462 Py_INCREF(Py_None);
463 return Py_None;
464#endif /* HAVE_MREMAP */
465#endif /* UNIX */
466 }
467}
468
469static PyObject *
470mmap_tell_method(mmap_object *self, PyObject *unused)
471{
472 CHECK_VALID(NULL);
473 return PyInt_FromSize_t(self->pos);
474}
475
476static PyObject *
477mmap_flush_method(mmap_object *self, PyObject *args)
478{
479 Py_ssize_t offset = 0;
480 Py_ssize_t size = self->size;
481 CHECK_VALID(NULL);
482 if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
483 return NULL;
484 if ((size_t)(offset + size) > self->size) {
485 PyErr_SetString(PyExc_ValueError, "flush values out of range");
486 return NULL;
487 } else {
488#ifdef MS_WINDOWS
489 return PyInt_FromLong((long)
490 FlushViewOfFile(self->data+offset, size));
491#endif /* MS_WINDOWS */
492#ifdef UNIX
493 /* XXX semantics of return value? */
494 /* XXX flags for msync? */
495 if (-1 == msync(self->data + offset, size,
496 MS_SYNC))
497 {
498 PyErr_SetFromErrno(mmap_module_error);
499 return NULL;
500 }
501 return PyInt_FromLong(0);
502#endif /* UNIX */
503 }
504}
505
506static PyObject *
507mmap_seek_method(mmap_object *self, PyObject *args)
508{
509 Py_ssize_t dist;
510 int how=0;
511 CHECK_VALID(NULL);
512 if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
513 return NULL;
514 else {
515 size_t where;
516 switch (how) {
517 case 0: /* relative to start */
518 if (dist < 0)
519 goto onoutofrange;
520 where = dist;
521 break;
522 case 1: /* relative to current position */
523 if ((Py_ssize_t)self->pos + dist < 0)
524 goto onoutofrange;
525 where = self->pos + dist;
526 break;
527 case 2: /* relative to end */
528 if ((Py_ssize_t)self->size + dist < 0)
529 goto onoutofrange;
530 where = self->size + dist;
531 break;
532 default:
533 PyErr_SetString(PyExc_ValueError, "unknown seek type");
534 return NULL;
535 }
536 if (where > self->size)
537 goto onoutofrange;
538 self->pos = where;
539 Py_INCREF(Py_None);
540 return Py_None;
541 }
542
543 onoutofrange:
544 PyErr_SetString(PyExc_ValueError, "seek out of range");
545 return NULL;
546}
547
548static PyObject *
549mmap_move_method(mmap_object *self, PyObject *args)
550{
551 unsigned long dest, src, count;
552 CHECK_VALID(NULL);
553 if (!PyArg_ParseTuple(args, "kkk:move", &dest, &src, &count) ||
554 !is_writeable(self)) {
555 return NULL;
556 } else {
557 /* bounds check the values */
558 if (/* end of source after end of data?? */
559 ((src+count) > self->size)
560 /* dest will fit? */
561 || (dest+count > self->size)) {
562 PyErr_SetString(PyExc_ValueError,
563 "source or destination out of range");
564 return NULL;
565 } else {
566 memmove(self->data+dest, self->data+src, count);
567 Py_INCREF(Py_None);
568 return Py_None;
569 }
570 }
571}
572
573static struct PyMethodDef mmap_object_methods[] = {
574 {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
575 {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
576 {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
577 {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
578 {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
579 {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
580 {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
581 {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
582 {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
583 {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
584 {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
585 {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
586 {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
587 {NULL, NULL} /* sentinel */
588};
589
590/* Functions for treating an mmap'ed file as a buffer */
591
592static Py_ssize_t
593mmap_buffer_getreadbuf(mmap_object *self, Py_ssize_t index, const void **ptr)
594{
595 CHECK_VALID(-1);
596 if (index != 0) {
597 PyErr_SetString(PyExc_SystemError,
598 "Accessing non-existent mmap segment");
599 return -1;
600 }
601 *ptr = self->data;
602 return self->size;
603}
604
605static Py_ssize_t
606mmap_buffer_getwritebuf(mmap_object *self, Py_ssize_t index, const void **ptr)
607{
608 CHECK_VALID(-1);
609 if (index != 0) {
610 PyErr_SetString(PyExc_SystemError,
611 "Accessing non-existent mmap segment");
612 return -1;
613 }
614 if (!is_writeable(self))
615 return -1;
616 *ptr = self->data;
617 return self->size;
618}
619
620static Py_ssize_t
621mmap_buffer_getsegcount(mmap_object *self, Py_ssize_t *lenp)
622{
623 CHECK_VALID(-1);
624 if (lenp)
625 *lenp = self->size;
626 return 1;
627}
628
629static Py_ssize_t
630mmap_buffer_getcharbuffer(mmap_object *self, Py_ssize_t index, const void **ptr)
631{
632 if (index != 0) {
633 PyErr_SetString(PyExc_SystemError,
634 "accessing non-existent buffer segment");
635 return -1;
636 }
637 *ptr = (const char *)self->data;
638 return self->size;
639}
640
641static PyObject *
642mmap_object_getattr(mmap_object *self, char *name)
643{
644 return Py_FindMethod(mmap_object_methods, (PyObject *)self, name);
645}
646
647static Py_ssize_t
648mmap_length(mmap_object *self)
649{
650 CHECK_VALID(-1);
651 return self->size;
652}
653
654static PyObject *
655mmap_item(mmap_object *self, Py_ssize_t i)
656{
657 CHECK_VALID(NULL);
658 if (i < 0 || (size_t)i >= self->size) {
659 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
660 return NULL;
661 }
662 return PyString_FromStringAndSize(self->data + i, 1);
663}
664
665static PyObject *
666mmap_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh)
667{
668 CHECK_VALID(NULL);
669 if (ilow < 0)
670 ilow = 0;
671 else if ((size_t)ilow > self->size)
672 ilow = self->size;
673 if (ihigh < 0)
674 ihigh = 0;
675 if (ihigh < ilow)
676 ihigh = ilow;
677 else if ((size_t)ihigh > self->size)
678 ihigh = self->size;
679
680 return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
681}
682
683static PyObject *
684mmap_concat(mmap_object *self, PyObject *bb)
685{
686 CHECK_VALID(NULL);
687 PyErr_SetString(PyExc_SystemError,
688 "mmaps don't support concatenation");
689 return NULL;
690}
691
692static PyObject *
693mmap_repeat(mmap_object *self, Py_ssize_t n)
694{
695 CHECK_VALID(NULL);
696 PyErr_SetString(PyExc_SystemError,
697 "mmaps don't support repeat operation");
698 return NULL;
699}
700
701static int
702mmap_ass_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
703{
704 const char *buf;
705
706 CHECK_VALID(-1);
707 if (ilow < 0)
708 ilow = 0;
709 else if ((size_t)ilow > self->size)
710 ilow = self->size;
711 if (ihigh < 0)
712 ihigh = 0;
713 if (ihigh < ilow)
714 ihigh = ilow;
715 else if ((size_t)ihigh > self->size)
716 ihigh = self->size;
717
718 if (v == NULL) {
719 PyErr_SetString(PyExc_TypeError,
720 "mmap object doesn't support slice deletion");
721 return -1;
722 }
723 if (! (PyString_Check(v)) ) {
724 PyErr_SetString(PyExc_IndexError,
725 "mmap slice assignment must be a string");
726 return -1;
727 }
728 if (PyString_Size(v) != (ihigh - ilow)) {
729 PyErr_SetString(PyExc_IndexError,
730 "mmap slice assignment is wrong size");
731 return -1;
732 }
733 if (!is_writeable(self))
734 return -1;
735 buf = PyString_AsString(v);
736 memcpy(self->data + ilow, buf, ihigh-ilow);
737 return 0;
738}
739
740static int
741mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
742{
743 const char *buf;
744
745 CHECK_VALID(-1);
746 if (i < 0 || (size_t)i >= self->size) {
747 PyErr_SetString(PyExc_IndexError, "mmap index out of range");
748 return -1;
749 }
750 if (v == NULL) {
751 PyErr_SetString(PyExc_TypeError,
752 "mmap object doesn't support item deletion");
753 return -1;
754 }
755 if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
756 PyErr_SetString(PyExc_IndexError,
757 "mmap assignment must be single-character string");
758 return -1;
759 }
760 if (!is_writeable(self))
761 return -1;
762 buf = PyString_AsString(v);
763 self->data[i] = buf[0];
764 return 0;
765}
766
767static PySequenceMethods mmap_as_sequence = {
768 (lenfunc)mmap_length, /*sq_length*/
769 (binaryfunc)mmap_concat, /*sq_concat*/
770 (ssizeargfunc)mmap_repeat, /*sq_repeat*/
771 (ssizeargfunc)mmap_item, /*sq_item*/
772 (ssizessizeargfunc)mmap_slice, /*sq_slice*/
773 (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
774 (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/
775};
776
777static PyBufferProcs mmap_as_buffer = {
778 (readbufferproc)mmap_buffer_getreadbuf,
779 (writebufferproc)mmap_buffer_getwritebuf,
780 (segcountproc)mmap_buffer_getsegcount,
781 (charbufferproc)mmap_buffer_getcharbuffer,
782};
783
784static PyTypeObject mmap_object_type = {
785 PyObject_HEAD_INIT(0) /* patched in module init */
786 0, /* ob_size */
787 "mmap.mmap", /* tp_name */
788 sizeof(mmap_object), /* tp_size */
789 0, /* tp_itemsize */
790 /* methods */
791 (destructor) mmap_object_dealloc, /* tp_dealloc */
792 0, /* tp_print */
793 (getattrfunc) mmap_object_getattr, /* tp_getattr */
794 0, /* tp_setattr */
795 0, /* tp_compare */
796 0, /* tp_repr */
797 0, /* tp_as_number */
798 &mmap_as_sequence, /*tp_as_sequence*/
799 0, /*tp_as_mapping*/
800 0, /*tp_hash*/
801 0, /*tp_call*/
802 0, /*tp_str*/
803 0, /*tp_getattro*/
804 0, /*tp_setattro*/
805 &mmap_as_buffer, /*tp_as_buffer*/
806 Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/
807 0, /*tp_doc*/
808};
809
810
811/* extract the map size from the given PyObject
812
813 Returns -1 on error, with an appropriate Python exception raised. On
814 success, the map size is returned. */
815static Py_ssize_t
816_GetMapSize(PyObject *o)
817{
818 if (PyIndex_Check(o)) {
819 Py_ssize_t i = PyNumber_AsSsize_t(o, PyExc_OverflowError);
820 if (i==-1 && PyErr_Occurred())
821 return -1;
822 if (i < 0) {
823 PyErr_SetString(PyExc_OverflowError,
824 "memory mapped size must be positive");
825 return -1;
826 }
827 return i;
828 }
829
830 PyErr_SetString(PyExc_TypeError, "map size must be an integral value");
831 return -1;
832}
833
834#ifdef UNIX
835static PyObject *
836new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
837{
838#ifdef HAVE_FSTAT
839 struct stat st;
840#endif
841 mmap_object *m_obj;
842 PyObject *map_size_obj = NULL;
843 Py_ssize_t map_size;
844 int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
845 int devzero = -1;
846 int access = (int)ACCESS_DEFAULT;
847 static char *keywords[] = {"fileno", "length",
848 "flags", "prot",
849 "access", NULL};
850
851 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords,
852 &fd, &map_size_obj, &flags, &prot,
853 &access))
854 return NULL;
855 map_size = _GetMapSize(map_size_obj);
856 if (map_size < 0)
857 return NULL;
858
859 if ((access != (int)ACCESS_DEFAULT) &&
860 ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
861 return PyErr_Format(PyExc_ValueError,
862 "mmap can't specify both access and flags, prot.");
863 switch ((access_mode)access) {
864 case ACCESS_READ:
865 flags = MAP_SHARED;
866 prot = PROT_READ;
867 break;
868 case ACCESS_WRITE:
869 flags = MAP_SHARED;
870 prot = PROT_READ | PROT_WRITE;
871 break;
872 case ACCESS_COPY:
873 flags = MAP_PRIVATE;
874 prot = PROT_READ | PROT_WRITE;
875 break;
876 case ACCESS_DEFAULT:
877 /* use the specified or default values of flags and prot */
878 break;
879 default:
880 return PyErr_Format(PyExc_ValueError,
881 "mmap invalid access parameter.");
882 }
883
884#ifdef HAVE_FSTAT
885# ifdef __VMS
886 /* on OpenVMS we must ensure that all bytes are written to the file */
887 fsync(fd);
888# endif
889 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
890 if (map_size == 0) {
891 map_size = st.st_size;
892 } else if ((size_t)map_size > st.st_size) {
893 PyErr_SetString(PyExc_ValueError,
894 "mmap length is greater than file size");
895 return NULL;
896 }
897 }
898#endif
899 m_obj = PyObject_New(mmap_object, &mmap_object_type);
900 if (m_obj == NULL) {return NULL;}
901 m_obj->data = NULL;
902 m_obj->size = (size_t) map_size;
903 m_obj->pos = (size_t) 0;
904 if (fd == -1) {
905 m_obj->fd = -1;
906 /* Assume the caller wants to map anonymous memory.
907 This is the same behaviour as Windows. mmap.mmap(-1, size)
908 on both Windows and Unix map anonymous memory.
909 */
910#ifdef MAP_ANONYMOUS
911 /* BSD way to map anonymous memory */
912 flags |= MAP_ANONYMOUS;
913#else
914 /* SVR4 method to map anonymous memory is to open /dev/zero */
915 fd = devzero = open("/dev/zero", O_RDWR);
916 if (devzero == -1) {
917 Py_DECREF(m_obj);
918 PyErr_SetFromErrno(mmap_module_error);
919 return NULL;
920 }
921#endif
922 } else {
923 m_obj->fd = dup(fd);
924 if (m_obj->fd == -1) {
925 Py_DECREF(m_obj);
926 PyErr_SetFromErrno(mmap_module_error);
927 return NULL;
928 }
929 }
930
931 m_obj->data = mmap(NULL, map_size,
932 prot, flags,
933 fd, 0);
934
935 if (devzero != -1) {
936 close(devzero);
937 }
938
939 if (m_obj->data == (char *)-1) {
940 m_obj->data = NULL;
941 Py_DECREF(m_obj);
942 PyErr_SetFromErrno(mmap_module_error);
943 return NULL;
944 }
945 m_obj->access = (access_mode)access;
946 return (PyObject *)m_obj;
947}
948#endif /* UNIX */
949
950#ifdef MS_WINDOWS
951static PyObject *
952new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
953{
954 mmap_object *m_obj;
955 PyObject *map_size_obj = NULL;
956 Py_ssize_t map_size;
957 DWORD size_hi; /* upper 32 bits of m_obj->size */
958 DWORD size_lo; /* lower 32 bits of m_obj->size */
959 char *tagname = "";
960 DWORD dwErr = 0;
961 int fileno;
962 HANDLE fh = 0;
963 int access = (access_mode)ACCESS_DEFAULT;
964 DWORD flProtect, dwDesiredAccess;
965 static char *keywords[] = { "fileno", "length",
966 "tagname",
967 "access", NULL };
968
969 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
970 &fileno, &map_size_obj,
971 &tagname, &access)) {
972 return NULL;
973 }
974
975 switch((access_mode)access) {
976 case ACCESS_READ:
977 flProtect = PAGE_READONLY;
978 dwDesiredAccess = FILE_MAP_READ;
979 break;
980 case ACCESS_DEFAULT: case ACCESS_WRITE:
981 flProtect = PAGE_READWRITE;
982 dwDesiredAccess = FILE_MAP_WRITE;
983 break;
984 case ACCESS_COPY:
985 flProtect = PAGE_WRITECOPY;
986 dwDesiredAccess = FILE_MAP_COPY;
987 break;
988 default:
989 return PyErr_Format(PyExc_ValueError,
990 "mmap invalid access parameter.");
991 }
992
993 map_size = _GetMapSize(map_size_obj);
994 if (map_size < 0)
995 return NULL;
996
997 /* assume -1 and 0 both mean invalid filedescriptor
998 to 'anonymously' map memory.
999 XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1000 XXX: Should this code be added?
1001 if (fileno == 0)
1002 PyErr_Warn(PyExc_DeprecationWarning,
1003 "don't use 0 for anonymous memory");
1004 */
1005 if (fileno != -1 && fileno != 0) {
1006 fh = (HANDLE)_get_osfhandle(fileno);
1007 if (fh==(HANDLE)-1) {
1008 PyErr_SetFromErrno(mmap_module_error);
1009 return NULL;
1010 }
1011 /* Win9x appears to need us seeked to zero */
1012 lseek(fileno, 0, SEEK_SET);
1013 }
1014
1015 m_obj = PyObject_New(mmap_object, &mmap_object_type);
1016 if (m_obj == NULL)
1017 return NULL;
1018 /* Set every field to an invalid marker, so we can safely
1019 destruct the object in the face of failure */
1020 m_obj->data = NULL;
1021 m_obj->file_handle = INVALID_HANDLE_VALUE;
1022 m_obj->map_handle = INVALID_HANDLE_VALUE;
1023 m_obj->tagname = NULL;
1024
1025 if (fh) {
1026 /* It is necessary to duplicate the handle, so the
1027 Python code can close it on us */
1028 if (!DuplicateHandle(
1029 GetCurrentProcess(), /* source process handle */
1030 fh, /* handle to be duplicated */
1031 GetCurrentProcess(), /* target proc handle */
1032 (LPHANDLE)&m_obj->file_handle, /* result */
1033 0, /* access - ignored due to options value */
1034 FALSE, /* inherited by child processes? */
1035 DUPLICATE_SAME_ACCESS)) { /* options */
1036 dwErr = GetLastError();
1037 Py_DECREF(m_obj);
1038 PyErr_SetFromWindowsErr(dwErr);
1039 return NULL;
1040 }
1041 if (!map_size) {
1042 DWORD low,high;
1043 low = GetFileSize(fh, &high);
1044 /* low might just happen to have the value INVALID_FILE_SIZE;
1045 so we need to check the last error also. */
1046 if (low == INVALID_FILE_SIZE &&
1047 (dwErr = GetLastError()) != NO_ERROR) {
1048 Py_DECREF(m_obj);
1049 return PyErr_SetFromWindowsErr(dwErr);
1050 }
1051
1052#if SIZEOF_SIZE_T > 4
1053 m_obj->size = (((size_t)high)<<32) + low;
1054#else
1055 if (high)
1056 /* File is too large to map completely */
1057 m_obj->size = (size_t)-1;
1058 else
1059 m_obj->size = low;
1060#endif
1061 } else {
1062 m_obj->size = map_size;
1063 }
1064 }
1065 else {
1066 m_obj->size = map_size;
1067 }
1068
1069 /* set the initial position */
1070 m_obj->pos = (size_t) 0;
1071
1072 /* set the tag name */
1073 if (tagname != NULL && *tagname != '\0') {
1074 m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1075 if (m_obj->tagname == NULL) {
1076 PyErr_NoMemory();
1077 Py_DECREF(m_obj);
1078 return NULL;
1079 }
1080 strcpy(m_obj->tagname, tagname);
1081 }
1082 else
1083 m_obj->tagname = NULL;
1084
1085 m_obj->access = (access_mode)access;
1086 /* DWORD is a 4-byte int. If we're on a box where size_t consumes
1087 * more than 4 bytes, we need to break it apart. Else (size_t
1088 * consumes 4 bytes), C doesn't define what happens if we shift
1089 * right by 32, so we need different code.
1090 */
1091#if SIZEOF_SIZE_T > 4
1092 size_hi = (DWORD)(m_obj->size >> 32);
1093 size_lo = (DWORD)(m_obj->size & 0xFFFFFFFF);
1094#else
1095 size_hi = 0;
1096 size_lo = (DWORD)m_obj->size;
1097#endif
1098 m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1099 NULL,
1100 flProtect,
1101 size_hi,
1102 size_lo,
1103 m_obj->tagname);
1104 if (m_obj->map_handle != NULL) {
1105 m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1106 dwDesiredAccess,
1107 0,
1108 0,
1109 0);
1110 if (m_obj->data != NULL)
1111 return (PyObject *)m_obj;
1112 else
1113 dwErr = GetLastError();
1114 } else
1115 dwErr = GetLastError();
1116 Py_DECREF(m_obj);
1117 PyErr_SetFromWindowsErr(dwErr);
1118 return NULL;
1119}
1120#endif /* MS_WINDOWS */
1121
1122/* List of functions exported by this module */
1123static struct PyMethodDef mmap_functions[] = {
1124 {"mmap", (PyCFunction) new_mmap_object,
1125 METH_VARARGS|METH_KEYWORDS},
1126 {NULL, NULL} /* Sentinel */
1127};
1128
1129static void
1130setint(PyObject *d, const char *name, long value)
1131{
1132 PyObject *o = PyInt_FromLong(value);
1133 if (o && PyDict_SetItemString(d, name, o) == 0) {
1134 Py_DECREF(o);
1135 }
1136}
1137
1138PyMODINIT_FUNC
1139 initmmap(void)
1140{
1141 PyObject *dict, *module;
1142
1143 /* Patch the object type */
1144 mmap_object_type.ob_type = &PyType_Type;
1145
1146 module = Py_InitModule("mmap", mmap_functions);
1147 if (module == NULL)
1148 return;
1149 dict = PyModule_GetDict(module);
1150 if (!dict)
1151 return;
1152 mmap_module_error = PyExc_EnvironmentError;
1153 PyDict_SetItemString(dict, "error", mmap_module_error);
1154#ifdef PROT_EXEC
1155 setint(dict, "PROT_EXEC", PROT_EXEC);
1156#endif
1157#ifdef PROT_READ
1158 setint(dict, "PROT_READ", PROT_READ);
1159#endif
1160#ifdef PROT_WRITE
1161 setint(dict, "PROT_WRITE", PROT_WRITE);
1162#endif
1163
1164#ifdef MAP_SHARED
1165 setint(dict, "MAP_SHARED", MAP_SHARED);
1166#endif
1167#ifdef MAP_PRIVATE
1168 setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
1169#endif
1170#ifdef MAP_DENYWRITE
1171 setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
1172#endif
1173#ifdef MAP_EXECUTABLE
1174 setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
1175#endif
1176#ifdef MAP_ANONYMOUS
1177 setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1178 setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
1179#endif
1180
1181 setint(dict, "PAGESIZE", (long)my_getpagesize());
1182
1183 setint(dict, "ACCESS_READ", ACCESS_READ);
1184 setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1185 setint(dict, "ACCESS_COPY", ACCESS_COPY);
1186}
Note: See TracBrowser for help on using the repository browser.