ยปCore Development>Code coverage>Modules/mmapmodule.c

Python code coverage for Modules/mmapmodule.c

#countcontent
1n/a/*
2n/a / Author: Sam Rushing <rushing@nightmare.com>
3n/a / Hacked for Unix by AMK
4n/a / $Id$
5n/a
6n/a / Modified to support mmap with offset - to map a 'window' of a file
7n/a / Author: Yotam Medini yotamm@mellanox.co.il
8n/a /
9n/a / mmapmodule.cpp -- map a view of a file into memory
10n/a /
11n/a / todo: need permission flags, perhaps a 'chsize' analog
12n/a / not all functions check range yet!!!
13n/a /
14n/a /
15n/a / This version of mmapmodule.c has been changed significantly
16n/a / from the original mmapfile.c on which it was based.
17n/a / The original version of mmapfile is maintained by Sam at
18n/a / ftp://squirl.nightmare.com/pub/python/python-ext.
19n/a*/
20n/a
21n/a#define PY_SSIZE_T_CLEAN
22n/a#include <Python.h>
23n/a#include "structmember.h"
24n/a
25n/a#ifndef MS_WINDOWS
26n/a#define UNIX
27n/a# ifdef HAVE_FCNTL_H
28n/a# include <fcntl.h>
29n/a# endif /* HAVE_FCNTL_H */
30n/a#endif
31n/a
32n/a#ifdef MS_WINDOWS
33n/a#include <windows.h>
34n/astatic int
35n/amy_getpagesize(void)
36n/a{
37n/a SYSTEM_INFO si;
38n/a GetSystemInfo(&si);
39n/a return si.dwPageSize;
40n/a}
41n/a
42n/astatic int
43n/amy_getallocationgranularity (void)
44n/a{
45n/a
46n/a SYSTEM_INFO si;
47n/a GetSystemInfo(&si);
48n/a return si.dwAllocationGranularity;
49n/a}
50n/a
51n/a#endif
52n/a
53n/a#ifdef UNIX
54n/a#include <sys/mman.h>
55n/a#include <sys/stat.h>
56n/a
57n/a#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
58n/astatic int
59n/amy_getpagesize(void)
60n/a{
61n/a return sysconf(_SC_PAGESIZE);
62n/a}
63n/a
64n/a#define my_getallocationgranularity my_getpagesize
65n/a#else
66n/a#define my_getpagesize getpagesize
67n/a#endif
68n/a
69n/a#endif /* UNIX */
70n/a
71n/a#include <string.h>
72n/a
73n/a#ifdef HAVE_SYS_TYPES_H
74n/a#include <sys/types.h>
75n/a#endif /* HAVE_SYS_TYPES_H */
76n/a
77n/a/* Prefer MAP_ANONYMOUS since MAP_ANON is deprecated according to man page. */
78n/a#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
79n/a# define MAP_ANONYMOUS MAP_ANON
80n/a#endif
81n/a
82n/atypedef enum
83n/a{
84n/a ACCESS_DEFAULT,
85n/a ACCESS_READ,
86n/a ACCESS_WRITE,
87n/a ACCESS_COPY
88n/a} access_mode;
89n/a
90n/atypedef struct {
91n/a PyObject_HEAD
92n/a char * data;
93n/a Py_ssize_t size;
94n/a Py_ssize_t pos; /* relative to offset */
95n/a#ifdef MS_WINDOWS
96n/a long long offset;
97n/a#else
98n/a off_t offset;
99n/a#endif
100n/a int exports;
101n/a
102n/a#ifdef MS_WINDOWS
103n/a HANDLE map_handle;
104n/a HANDLE file_handle;
105n/a char * tagname;
106n/a#endif
107n/a
108n/a#ifdef UNIX
109n/a int fd;
110n/a#endif
111n/a
112n/a PyObject *weakreflist;
113n/a access_mode access;
114n/a} mmap_object;
115n/a
116n/a
117n/astatic void
118n/ammap_object_dealloc(mmap_object *m_obj)
119n/a{
120n/a#ifdef MS_WINDOWS
121n/a if (m_obj->data != NULL)
122n/a UnmapViewOfFile (m_obj->data);
123n/a if (m_obj->map_handle != NULL)
124n/a CloseHandle (m_obj->map_handle);
125n/a if (m_obj->file_handle != INVALID_HANDLE_VALUE)
126n/a CloseHandle (m_obj->file_handle);
127n/a if (m_obj->tagname)
128n/a PyMem_Free(m_obj->tagname);
129n/a#endif /* MS_WINDOWS */
130n/a
131n/a#ifdef UNIX
132n/a if (m_obj->fd >= 0)
133n/a (void) close(m_obj->fd);
134n/a if (m_obj->data!=NULL) {
135n/a munmap(m_obj->data, m_obj->size);
136n/a }
137n/a#endif /* UNIX */
138n/a
139n/a if (m_obj->weakreflist != NULL)
140n/a PyObject_ClearWeakRefs((PyObject *) m_obj);
141n/a Py_TYPE(m_obj)->tp_free((PyObject*)m_obj);
142n/a}
143n/a
144n/astatic PyObject *
145n/ammap_close_method(mmap_object *self, PyObject *unused)
146n/a{
147n/a if (self->exports > 0) {
148n/a PyErr_SetString(PyExc_BufferError, "cannot close "\
149n/a "exported pointers exist");
150n/a return NULL;
151n/a }
152n/a#ifdef MS_WINDOWS
153n/a /* For each resource we maintain, we need to check
154n/a the value is valid, and if so, free the resource
155n/a and set the member value to an invalid value so
156n/a the dealloc does not attempt to resource clearing
157n/a again.
158n/a TODO - should we check for errors in the close operations???
159n/a */
160n/a if (self->data != NULL) {
161n/a UnmapViewOfFile(self->data);
162n/a self->data = NULL;
163n/a }
164n/a if (self->map_handle != NULL) {
165n/a CloseHandle(self->map_handle);
166n/a self->map_handle = NULL;
167n/a }
168n/a if (self->file_handle != INVALID_HANDLE_VALUE) {
169n/a CloseHandle(self->file_handle);
170n/a self->file_handle = INVALID_HANDLE_VALUE;
171n/a }
172n/a#endif /* MS_WINDOWS */
173n/a
174n/a#ifdef UNIX
175n/a if (0 <= self->fd)
176n/a (void) close(self->fd);
177n/a self->fd = -1;
178n/a if (self->data != NULL) {
179n/a munmap(self->data, self->size);
180n/a self->data = NULL;
181n/a }
182n/a#endif
183n/a
184n/a Py_RETURN_NONE;
185n/a}
186n/a
187n/a#ifdef MS_WINDOWS
188n/a#define CHECK_VALID(err) \
189n/ado { \
190n/a if (self->map_handle == NULL) { \
191n/a PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
192n/a return err; \
193n/a } \
194n/a} while (0)
195n/a#endif /* MS_WINDOWS */
196n/a
197n/a#ifdef UNIX
198n/a#define CHECK_VALID(err) \
199n/ado { \
200n/a if (self->data == NULL) { \
201n/a PyErr_SetString(PyExc_ValueError, "mmap closed or invalid"); \
202n/a return err; \
203n/a } \
204n/a} while (0)
205n/a#endif /* UNIX */
206n/a
207n/astatic PyObject *
208n/ammap_read_byte_method(mmap_object *self,
209n/a PyObject *unused)
210n/a{
211n/a CHECK_VALID(NULL);
212n/a if (self->pos >= self->size) {
213n/a PyErr_SetString(PyExc_ValueError, "read byte out of range");
214n/a return NULL;
215n/a }
216n/a return PyLong_FromLong((unsigned char)self->data[self->pos++]);
217n/a}
218n/a
219n/astatic PyObject *
220n/ammap_read_line_method(mmap_object *self,
221n/a PyObject *unused)
222n/a{
223n/a Py_ssize_t remaining;
224n/a char *start, *eol;
225n/a PyObject *result;
226n/a
227n/a CHECK_VALID(NULL);
228n/a
229n/a remaining = (self->pos < self->size) ? self->size - self->pos : 0;
230n/a if (!remaining)
231n/a return PyBytes_FromString("");
232n/a start = self->data + self->pos;
233n/a eol = memchr(start, '\n', remaining);
234n/a if (!eol)
235n/a eol = self->data + self->size;
236n/a else
237n/a ++eol; /* advance past newline */
238n/a result = PyBytes_FromStringAndSize(start, (eol - start));
239n/a self->pos += (eol - start);
240n/a return result;
241n/a}
242n/a
243n/a/* Basically the "n" format code with the ability to turn None into -1. */
244n/astatic int
245n/ammap_convert_ssize_t(PyObject *obj, void *result) {
246n/a Py_ssize_t limit;
247n/a if (obj == Py_None) {
248n/a limit = -1;
249n/a }
250n/a else if (PyNumber_Check(obj)) {
251n/a limit = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
252n/a if (limit == -1 && PyErr_Occurred())
253n/a return 0;
254n/a }
255n/a else {
256n/a PyErr_Format(PyExc_TypeError,
257n/a "integer argument expected, got '%.200s'",
258n/a Py_TYPE(obj)->tp_name);
259n/a return 0;
260n/a }
261n/a *((Py_ssize_t *)result) = limit;
262n/a return 1;
263n/a}
264n/a
265n/astatic PyObject *
266n/ammap_read_method(mmap_object *self,
267n/a PyObject *args)
268n/a{
269n/a Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
270n/a PyObject *result;
271n/a
272n/a CHECK_VALID(NULL);
273n/a if (!PyArg_ParseTuple(args, "|O&:read", mmap_convert_ssize_t, &num_bytes))
274n/a return(NULL);
275n/a
276n/a /* silently 'adjust' out-of-range requests */
277n/a remaining = (self->pos < self->size) ? self->size - self->pos : 0;
278n/a if (num_bytes < 0 || num_bytes > remaining)
279n/a num_bytes = remaining;
280n/a result = PyBytes_FromStringAndSize(&self->data[self->pos], num_bytes);
281n/a self->pos += num_bytes;
282n/a return result;
283n/a}
284n/a
285n/astatic PyObject *
286n/ammap_gfind(mmap_object *self,
287n/a PyObject *args,
288n/a int reverse)
289n/a{
290n/a Py_ssize_t start = self->pos;
291n/a Py_ssize_t end = self->size;
292n/a Py_buffer view;
293n/a
294n/a CHECK_VALID(NULL);
295n/a if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
296n/a &view, &start, &end)) {
297n/a return NULL;
298n/a } else {
299n/a const char *p, *start_p, *end_p;
300n/a int sign = reverse ? -1 : 1;
301n/a const char *needle = view.buf;
302n/a Py_ssize_t len = view.len;
303n/a
304n/a if (start < 0)
305n/a start += self->size;
306n/a if (start < 0)
307n/a start = 0;
308n/a else if (start > self->size)
309n/a start = self->size;
310n/a
311n/a if (end < 0)
312n/a end += self->size;
313n/a if (end < 0)
314n/a end = 0;
315n/a else if (end > self->size)
316n/a end = self->size;
317n/a
318n/a start_p = self->data + start;
319n/a end_p = self->data + end;
320n/a
321n/a for (p = (reverse ? end_p - len : start_p);
322n/a (p >= start_p) && (p + len <= end_p); p += sign) {
323n/a Py_ssize_t i;
324n/a for (i = 0; i < len && needle[i] == p[i]; ++i)
325n/a /* nothing */;
326n/a if (i == len) {
327n/a PyBuffer_Release(&view);
328n/a return PyLong_FromSsize_t(p - self->data);
329n/a }
330n/a }
331n/a PyBuffer_Release(&view);
332n/a return PyLong_FromLong(-1);
333n/a }
334n/a}
335n/a
336n/astatic PyObject *
337n/ammap_find_method(mmap_object *self,
338n/a PyObject *args)
339n/a{
340n/a return mmap_gfind(self, args, 0);
341n/a}
342n/a
343n/astatic PyObject *
344n/ammap_rfind_method(mmap_object *self,
345n/a PyObject *args)
346n/a{
347n/a return mmap_gfind(self, args, 1);
348n/a}
349n/a
350n/astatic int
351n/ais_writable(mmap_object *self)
352n/a{
353n/a if (self->access != ACCESS_READ)
354n/a return 1;
355n/a PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
356n/a return 0;
357n/a}
358n/a
359n/astatic int
360n/ais_resizeable(mmap_object *self)
361n/a{
362n/a if (self->exports > 0) {
363n/a PyErr_SetString(PyExc_BufferError,
364n/a "mmap can't resize with extant buffers exported.");
365n/a return 0;
366n/a }
367n/a if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
368n/a return 1;
369n/a PyErr_Format(PyExc_TypeError,
370n/a "mmap can't resize a readonly or copy-on-write memory map.");
371n/a return 0;
372n/a}
373n/a
374n/a
375n/astatic PyObject *
376n/ammap_write_method(mmap_object *self,
377n/a PyObject *args)
378n/a{
379n/a Py_buffer data;
380n/a
381n/a CHECK_VALID(NULL);
382n/a if (!PyArg_ParseTuple(args, "y*:write", &data))
383n/a return(NULL);
384n/a
385n/a if (!is_writable(self)) {
386n/a PyBuffer_Release(&data);
387n/a return NULL;
388n/a }
389n/a
390n/a if (self->pos > self->size || self->size - self->pos < data.len) {
391n/a PyBuffer_Release(&data);
392n/a PyErr_SetString(PyExc_ValueError, "data out of range");
393n/a return NULL;
394n/a }
395n/a
396n/a memcpy(&self->data[self->pos], data.buf, data.len);
397n/a self->pos += data.len;
398n/a PyBuffer_Release(&data);
399n/a return PyLong_FromSsize_t(data.len);
400n/a}
401n/a
402n/astatic PyObject *
403n/ammap_write_byte_method(mmap_object *self,
404n/a PyObject *args)
405n/a{
406n/a char value;
407n/a
408n/a CHECK_VALID(NULL);
409n/a if (!PyArg_ParseTuple(args, "b:write_byte", &value))
410n/a return(NULL);
411n/a
412n/a if (!is_writable(self))
413n/a return NULL;
414n/a
415n/a if (self->pos < self->size) {
416n/a self->data[self->pos++] = value;
417n/a Py_RETURN_NONE;
418n/a }
419n/a else {
420n/a PyErr_SetString(PyExc_ValueError, "write byte out of range");
421n/a return NULL;
422n/a }
423n/a}
424n/a
425n/astatic PyObject *
426n/ammap_size_method(mmap_object *self,
427n/a PyObject *unused)
428n/a{
429n/a CHECK_VALID(NULL);
430n/a
431n/a#ifdef MS_WINDOWS
432n/a if (self->file_handle != INVALID_HANDLE_VALUE) {
433n/a DWORD low,high;
434n/a long long size;
435n/a low = GetFileSize(self->file_handle, &high);
436n/a if (low == INVALID_FILE_SIZE) {
437n/a /* It might be that the function appears to have failed,
438n/a when indeed its size equals INVALID_FILE_SIZE */
439n/a DWORD error = GetLastError();
440n/a if (error != NO_ERROR)
441n/a return PyErr_SetFromWindowsErr(error);
442n/a }
443n/a if (!high && low < LONG_MAX)
444n/a return PyLong_FromLong((long)low);
445n/a size = (((long long)high)<<32) + low;
446n/a return PyLong_FromLongLong(size);
447n/a } else {
448n/a return PyLong_FromSsize_t(self->size);
449n/a }
450n/a#endif /* MS_WINDOWS */
451n/a
452n/a#ifdef UNIX
453n/a {
454n/a struct _Py_stat_struct status;
455n/a if (_Py_fstat(self->fd, &status) == -1)
456n/a return NULL;
457n/a#ifdef HAVE_LARGEFILE_SUPPORT
458n/a return PyLong_FromLongLong(status.st_size);
459n/a#else
460n/a return PyLong_FromLong(status.st_size);
461n/a#endif
462n/a }
463n/a#endif /* UNIX */
464n/a}
465n/a
466n/a/* This assumes that you want the entire file mapped,
467n/a / and when recreating the map will make the new file
468n/a / have the new size
469n/a /
470n/a / Is this really necessary? This could easily be done
471n/a / from python by just closing and re-opening with the
472n/a / new size?
473n/a */
474n/a
475n/astatic PyObject *
476n/ammap_resize_method(mmap_object *self,
477n/a PyObject *args)
478n/a{
479n/a Py_ssize_t new_size;
480n/a CHECK_VALID(NULL);
481n/a if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
482n/a !is_resizeable(self)) {
483n/a return NULL;
484n/a }
485n/a if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
486n/a PyErr_SetString(PyExc_ValueError, "new size out of range");
487n/a return NULL;
488n/a }
489n/a
490n/a {
491n/a#ifdef MS_WINDOWS
492n/a DWORD dwErrCode = 0;
493n/a DWORD off_hi, off_lo, newSizeLow, newSizeHigh;
494n/a /* First, unmap the file view */
495n/a UnmapViewOfFile(self->data);
496n/a self->data = NULL;
497n/a /* Close the mapping object */
498n/a CloseHandle(self->map_handle);
499n/a self->map_handle = NULL;
500n/a /* Move to the desired EOF position */
501n/a newSizeHigh = (DWORD)((self->offset + new_size) >> 32);
502n/a newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF);
503n/a off_hi = (DWORD)(self->offset >> 32);
504n/a off_lo = (DWORD)(self->offset & 0xFFFFFFFF);
505n/a SetFilePointer(self->file_handle,
506n/a newSizeLow, &newSizeHigh, FILE_BEGIN);
507n/a /* Change the size of the file */
508n/a SetEndOfFile(self->file_handle);
509n/a /* Create another mapping object and remap the file view */
510n/a self->map_handle = CreateFileMapping(
511n/a self->file_handle,
512n/a NULL,
513n/a PAGE_READWRITE,
514n/a 0,
515n/a 0,
516n/a self->tagname);
517n/a if (self->map_handle != NULL) {
518n/a self->data = (char *) MapViewOfFile(self->map_handle,
519n/a FILE_MAP_WRITE,
520n/a off_hi,
521n/a off_lo,
522n/a new_size);
523n/a if (self->data != NULL) {
524n/a self->size = new_size;
525n/a Py_RETURN_NONE;
526n/a } else {
527n/a dwErrCode = GetLastError();
528n/a CloseHandle(self->map_handle);
529n/a self->map_handle = NULL;
530n/a }
531n/a } else {
532n/a dwErrCode = GetLastError();
533n/a }
534n/a PyErr_SetFromWindowsErr(dwErrCode);
535n/a return NULL;
536n/a#endif /* MS_WINDOWS */
537n/a
538n/a#ifdef UNIX
539n/a#ifndef HAVE_MREMAP
540n/a PyErr_SetString(PyExc_SystemError,
541n/a "mmap: resizing not available--no mremap()");
542n/a return NULL;
543n/a#else
544n/a void *newmap;
545n/a
546n/a if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
547n/a PyErr_SetFromErrno(PyExc_OSError);
548n/a return NULL;
549n/a }
550n/a
551n/a#ifdef MREMAP_MAYMOVE
552n/a newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
553n/a#else
554n/a#if defined(__NetBSD__)
555n/a newmap = mremap(self->data, self->size, self->data, new_size, 0);
556n/a#else
557n/a newmap = mremap(self->data, self->size, new_size, 0);
558n/a#endif /* __NetBSD__ */
559n/a#endif
560n/a if (newmap == (void *)-1)
561n/a {
562n/a PyErr_SetFromErrno(PyExc_OSError);
563n/a return NULL;
564n/a }
565n/a self->data = newmap;
566n/a self->size = new_size;
567n/a Py_RETURN_NONE;
568n/a#endif /* HAVE_MREMAP */
569n/a#endif /* UNIX */
570n/a }
571n/a}
572n/a
573n/astatic PyObject *
574n/ammap_tell_method(mmap_object *self, PyObject *unused)
575n/a{
576n/a CHECK_VALID(NULL);
577n/a return PyLong_FromSize_t(self->pos);
578n/a}
579n/a
580n/astatic PyObject *
581n/ammap_flush_method(mmap_object *self, PyObject *args)
582n/a{
583n/a Py_ssize_t offset = 0;
584n/a Py_ssize_t size = self->size;
585n/a CHECK_VALID(NULL);
586n/a if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
587n/a return NULL;
588n/a if (size < 0 || offset < 0 || self->size - offset < size) {
589n/a PyErr_SetString(PyExc_ValueError, "flush values out of range");
590n/a return NULL;
591n/a }
592n/a
593n/a if (self->access == ACCESS_READ || self->access == ACCESS_COPY)
594n/a return PyLong_FromLong(0);
595n/a
596n/a#ifdef MS_WINDOWS
597n/a return PyLong_FromLong((long) FlushViewOfFile(self->data+offset, size));
598n/a#elif defined(UNIX)
599n/a /* XXX semantics of return value? */
600n/a /* XXX flags for msync? */
601n/a if (-1 == msync(self->data + offset, size, MS_SYNC)) {
602n/a PyErr_SetFromErrno(PyExc_OSError);
603n/a return NULL;
604n/a }
605n/a return PyLong_FromLong(0);
606n/a#else
607n/a PyErr_SetString(PyExc_ValueError, "flush not supported on this system");
608n/a return NULL;
609n/a#endif
610n/a}
611n/a
612n/astatic PyObject *
613n/ammap_seek_method(mmap_object *self, PyObject *args)
614n/a{
615n/a Py_ssize_t dist;
616n/a int how=0;
617n/a CHECK_VALID(NULL);
618n/a if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
619n/a return NULL;
620n/a else {
621n/a Py_ssize_t where;
622n/a switch (how) {
623n/a case 0: /* relative to start */
624n/a where = dist;
625n/a break;
626n/a case 1: /* relative to current position */
627n/a if (PY_SSIZE_T_MAX - self->pos < dist)
628n/a goto onoutofrange;
629n/a where = self->pos + dist;
630n/a break;
631n/a case 2: /* relative to end */
632n/a if (PY_SSIZE_T_MAX - self->size < dist)
633n/a goto onoutofrange;
634n/a where = self->size + dist;
635n/a break;
636n/a default:
637n/a PyErr_SetString(PyExc_ValueError, "unknown seek type");
638n/a return NULL;
639n/a }
640n/a if (where > self->size || where < 0)
641n/a goto onoutofrange;
642n/a self->pos = where;
643n/a Py_RETURN_NONE;
644n/a }
645n/a
646n/a onoutofrange:
647n/a PyErr_SetString(PyExc_ValueError, "seek out of range");
648n/a return NULL;
649n/a}
650n/a
651n/astatic PyObject *
652n/ammap_move_method(mmap_object *self, PyObject *args)
653n/a{
654n/a Py_ssize_t dest, src, cnt;
655n/a CHECK_VALID(NULL);
656n/a if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
657n/a !is_writable(self)) {
658n/a return NULL;
659n/a } else {
660n/a /* bounds check the values */
661n/a if (dest < 0 || src < 0 || cnt < 0)
662n/a goto bounds;
663n/a if (self->size - dest < cnt || self->size - src < cnt)
664n/a goto bounds;
665n/a
666n/a memmove(&self->data[dest], &self->data[src], cnt);
667n/a
668n/a Py_RETURN_NONE;
669n/a
670n/a bounds:
671n/a PyErr_SetString(PyExc_ValueError,
672n/a "source, destination, or count out of range");
673n/a return NULL;
674n/a }
675n/a}
676n/a
677n/astatic PyObject *
678n/ammap_closed_get(mmap_object *self)
679n/a{
680n/a#ifdef MS_WINDOWS
681n/a return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
682n/a#elif defined(UNIX)
683n/a return PyBool_FromLong(self->data == NULL ? 1 : 0);
684n/a#endif
685n/a}
686n/a
687n/astatic PyObject *
688n/ammap__enter__method(mmap_object *self, PyObject *args)
689n/a{
690n/a CHECK_VALID(NULL);
691n/a
692n/a Py_INCREF(self);
693n/a return (PyObject *)self;
694n/a}
695n/a
696n/astatic PyObject *
697n/ammap__exit__method(PyObject *self, PyObject *args)
698n/a{
699n/a _Py_IDENTIFIER(close);
700n/a
701n/a return _PyObject_CallMethodId(self, &PyId_close, NULL);
702n/a}
703n/a
704n/a#ifdef MS_WINDOWS
705n/astatic PyObject *
706n/ammap__sizeof__method(mmap_object *self, void *unused)
707n/a{
708n/a Py_ssize_t res;
709n/a
710n/a res = _PyObject_SIZE(Py_TYPE(self));
711n/a if (self->tagname)
712n/a res += strlen(self->tagname) + 1;
713n/a return PyLong_FromSsize_t(res);
714n/a}
715n/a#endif
716n/a
717n/astatic struct PyMethodDef mmap_object_methods[] = {
718n/a {"close", (PyCFunction) mmap_close_method, METH_NOARGS},
719n/a {"find", (PyCFunction) mmap_find_method, METH_VARARGS},
720n/a {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS},
721n/a {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS},
722n/a {"move", (PyCFunction) mmap_move_method, METH_VARARGS},
723n/a {"read", (PyCFunction) mmap_read_method, METH_VARARGS},
724n/a {"read_byte", (PyCFunction) mmap_read_byte_method, METH_NOARGS},
725n/a {"readline", (PyCFunction) mmap_read_line_method, METH_NOARGS},
726n/a {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS},
727n/a {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS},
728n/a {"size", (PyCFunction) mmap_size_method, METH_NOARGS},
729n/a {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS},
730n/a {"write", (PyCFunction) mmap_write_method, METH_VARARGS},
731n/a {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS},
732n/a {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS},
733n/a {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS},
734n/a#ifdef MS_WINDOWS
735n/a {"__sizeof__", (PyCFunction) mmap__sizeof__method, METH_NOARGS},
736n/a#endif
737n/a {NULL, NULL} /* sentinel */
738n/a};
739n/a
740n/astatic PyGetSetDef mmap_object_getset[] = {
741n/a {"closed", (getter) mmap_closed_get, NULL, NULL},
742n/a {NULL}
743n/a};
744n/a
745n/a
746n/a/* Functions for treating an mmap'ed file as a buffer */
747n/a
748n/astatic int
749n/ammap_buffer_getbuf(mmap_object *self, Py_buffer *view, int flags)
750n/a{
751n/a CHECK_VALID(-1);
752n/a if (PyBuffer_FillInfo(view, (PyObject*)self, self->data, self->size,
753n/a (self->access == ACCESS_READ), flags) < 0)
754n/a return -1;
755n/a self->exports++;
756n/a return 0;
757n/a}
758n/a
759n/astatic void
760n/ammap_buffer_releasebuf(mmap_object *self, Py_buffer *view)
761n/a{
762n/a self->exports--;
763n/a}
764n/a
765n/astatic Py_ssize_t
766n/ammap_length(mmap_object *self)
767n/a{
768n/a CHECK_VALID(-1);
769n/a return self->size;
770n/a}
771n/a
772n/astatic PyObject *
773n/ammap_item(mmap_object *self, Py_ssize_t i)
774n/a{
775n/a CHECK_VALID(NULL);
776n/a if (i < 0 || i >= self->size) {
777n/a PyErr_SetString(PyExc_IndexError, "mmap index out of range");
778n/a return NULL;
779n/a }
780n/a return PyBytes_FromStringAndSize(self->data + i, 1);
781n/a}
782n/a
783n/astatic PyObject *
784n/ammap_subscript(mmap_object *self, PyObject *item)
785n/a{
786n/a CHECK_VALID(NULL);
787n/a if (PyIndex_Check(item)) {
788n/a Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
789n/a if (i == -1 && PyErr_Occurred())
790n/a return NULL;
791n/a if (i < 0)
792n/a i += self->size;
793n/a if (i < 0 || i >= self->size) {
794n/a PyErr_SetString(PyExc_IndexError,
795n/a "mmap index out of range");
796n/a return NULL;
797n/a }
798n/a return PyLong_FromLong(Py_CHARMASK(self->data[i]));
799n/a }
800n/a else if (PySlice_Check(item)) {
801n/a Py_ssize_t start, stop, step, slicelen;
802n/a
803n/a if (PySlice_GetIndicesEx(item, self->size,
804n/a &start, &stop, &step, &slicelen) < 0) {
805n/a return NULL;
806n/a }
807n/a
808n/a if (slicelen <= 0)
809n/a return PyBytes_FromStringAndSize("", 0);
810n/a else if (step == 1)
811n/a return PyBytes_FromStringAndSize(self->data + start,
812n/a slicelen);
813n/a else {
814n/a char *result_buf = (char *)PyMem_Malloc(slicelen);
815n/a Py_ssize_t cur, i;
816n/a PyObject *result;
817n/a
818n/a if (result_buf == NULL)
819n/a return PyErr_NoMemory();
820n/a for (cur = start, i = 0; i < slicelen;
821n/a cur += step, i++) {
822n/a result_buf[i] = self->data[cur];
823n/a }
824n/a result = PyBytes_FromStringAndSize(result_buf,
825n/a slicelen);
826n/a PyMem_Free(result_buf);
827n/a return result;
828n/a }
829n/a }
830n/a else {
831n/a PyErr_SetString(PyExc_TypeError,
832n/a "mmap indices must be integers");
833n/a return NULL;
834n/a }
835n/a}
836n/a
837n/astatic PyObject *
838n/ammap_concat(mmap_object *self, PyObject *bb)
839n/a{
840n/a CHECK_VALID(NULL);
841n/a PyErr_SetString(PyExc_SystemError,
842n/a "mmaps don't support concatenation");
843n/a return NULL;
844n/a}
845n/a
846n/astatic PyObject *
847n/ammap_repeat(mmap_object *self, Py_ssize_t n)
848n/a{
849n/a CHECK_VALID(NULL);
850n/a PyErr_SetString(PyExc_SystemError,
851n/a "mmaps don't support repeat operation");
852n/a return NULL;
853n/a}
854n/a
855n/astatic int
856n/ammap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v)
857n/a{
858n/a const char *buf;
859n/a
860n/a CHECK_VALID(-1);
861n/a if (i < 0 || i >= self->size) {
862n/a PyErr_SetString(PyExc_IndexError, "mmap index out of range");
863n/a return -1;
864n/a }
865n/a if (v == NULL) {
866n/a PyErr_SetString(PyExc_TypeError,
867n/a "mmap object doesn't support item deletion");
868n/a return -1;
869n/a }
870n/a if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) {
871n/a PyErr_SetString(PyExc_IndexError,
872n/a "mmap assignment must be length-1 bytes()");
873n/a return -1;
874n/a }
875n/a if (!is_writable(self))
876n/a return -1;
877n/a buf = PyBytes_AsString(v);
878n/a self->data[i] = buf[0];
879n/a return 0;
880n/a}
881n/a
882n/astatic int
883n/ammap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value)
884n/a{
885n/a CHECK_VALID(-1);
886n/a
887n/a if (!is_writable(self))
888n/a return -1;
889n/a
890n/a if (PyIndex_Check(item)) {
891n/a Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
892n/a Py_ssize_t v;
893n/a
894n/a if (i == -1 && PyErr_Occurred())
895n/a return -1;
896n/a if (i < 0)
897n/a i += self->size;
898n/a if (i < 0 || i >= self->size) {
899n/a PyErr_SetString(PyExc_IndexError,
900n/a "mmap index out of range");
901n/a return -1;
902n/a }
903n/a if (value == NULL) {
904n/a PyErr_SetString(PyExc_TypeError,
905n/a "mmap doesn't support item deletion");
906n/a return -1;
907n/a }
908n/a if (!PyIndex_Check(value)) {
909n/a PyErr_SetString(PyExc_TypeError,
910n/a "mmap item value must be an int");
911n/a return -1;
912n/a }
913n/a v = PyNumber_AsSsize_t(value, PyExc_TypeError);
914n/a if (v == -1 && PyErr_Occurred())
915n/a return -1;
916n/a if (v < 0 || v > 255) {
917n/a PyErr_SetString(PyExc_ValueError,
918n/a "mmap item value must be "
919n/a "in range(0, 256)");
920n/a return -1;
921n/a }
922n/a self->data[i] = (char) v;
923n/a return 0;
924n/a }
925n/a else if (PySlice_Check(item)) {
926n/a Py_ssize_t start, stop, step, slicelen;
927n/a Py_buffer vbuf;
928n/a
929n/a if (PySlice_GetIndicesEx(item,
930n/a self->size, &start, &stop,
931n/a &step, &slicelen) < 0) {
932n/a return -1;
933n/a }
934n/a if (value == NULL) {
935n/a PyErr_SetString(PyExc_TypeError,
936n/a "mmap object doesn't support slice deletion");
937n/a return -1;
938n/a }
939n/a if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0)
940n/a return -1;
941n/a if (vbuf.len != slicelen) {
942n/a PyErr_SetString(PyExc_IndexError,
943n/a "mmap slice assignment is wrong size");
944n/a PyBuffer_Release(&vbuf);
945n/a return -1;
946n/a }
947n/a
948n/a if (slicelen == 0) {
949n/a }
950n/a else if (step == 1) {
951n/a memcpy(self->data + start, vbuf.buf, slicelen);
952n/a }
953n/a else {
954n/a Py_ssize_t cur, i;
955n/a
956n/a for (cur = start, i = 0;
957n/a i < slicelen;
958n/a cur += step, i++)
959n/a {
960n/a self->data[cur] = ((char *)vbuf.buf)[i];
961n/a }
962n/a }
963n/a PyBuffer_Release(&vbuf);
964n/a return 0;
965n/a }
966n/a else {
967n/a PyErr_SetString(PyExc_TypeError,
968n/a "mmap indices must be integer");
969n/a return -1;
970n/a }
971n/a}
972n/a
973n/astatic PySequenceMethods mmap_as_sequence = {
974n/a (lenfunc)mmap_length, /*sq_length*/
975n/a (binaryfunc)mmap_concat, /*sq_concat*/
976n/a (ssizeargfunc)mmap_repeat, /*sq_repeat*/
977n/a (ssizeargfunc)mmap_item, /*sq_item*/
978n/a 0, /*sq_slice*/
979n/a (ssizeobjargproc)mmap_ass_item, /*sq_ass_item*/
980n/a 0, /*sq_ass_slice*/
981n/a};
982n/a
983n/astatic PyMappingMethods mmap_as_mapping = {
984n/a (lenfunc)mmap_length,
985n/a (binaryfunc)mmap_subscript,
986n/a (objobjargproc)mmap_ass_subscript,
987n/a};
988n/a
989n/astatic PyBufferProcs mmap_as_buffer = {
990n/a (getbufferproc)mmap_buffer_getbuf,
991n/a (releasebufferproc)mmap_buffer_releasebuf,
992n/a};
993n/a
994n/astatic PyObject *
995n/anew_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);
996n/a
997n/aPyDoc_STRVAR(mmap_doc,
998n/a"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\
999n/a\n\
1000n/aMaps length bytes from the file specified by the file handle fileno,\n\
1001n/aand returns a mmap object. If length is larger than the current size\n\
1002n/aof the file, the file is extended to contain length bytes. If length\n\
1003n/ais 0, the maximum length of the map is the current size of the file,\n\
1004n/aexcept that if the file is empty Windows raises an exception (you cannot\n\
1005n/acreate an empty mapping on Windows).\n\
1006n/a\n\
1007n/aUnix: mmap(fileno, length[, flags[, prot[, access[, offset]]]])\n\
1008n/a\n\
1009n/aMaps length bytes from the file specified by the file descriptor fileno,\n\
1010n/aand returns a mmap object. If length is 0, the maximum length of the map\n\
1011n/awill be the current size of the file when mmap is called.\n\
1012n/aflags specifies the nature of the mapping. MAP_PRIVATE creates a\n\
1013n/aprivate copy-on-write mapping, so changes to the contents of the mmap\n\
1014n/aobject will be private to this process, and MAP_SHARED creates a mapping\n\
1015n/athat's shared with all other processes mapping the same areas of the file.\n\
1016n/aThe default value is MAP_SHARED.\n\
1017n/a\n\
1018n/aTo map anonymous memory, pass -1 as the fileno (both versions).");
1019n/a
1020n/a
1021n/astatic PyTypeObject mmap_object_type = {
1022n/a PyVarObject_HEAD_INIT(NULL, 0)
1023n/a "mmap.mmap", /* tp_name */
1024n/a sizeof(mmap_object), /* tp_size */
1025n/a 0, /* tp_itemsize */
1026n/a /* methods */
1027n/a (destructor) mmap_object_dealloc, /* tp_dealloc */
1028n/a 0, /* tp_print */
1029n/a 0, /* tp_getattr */
1030n/a 0, /* tp_setattr */
1031n/a 0, /* tp_reserved */
1032n/a 0, /* tp_repr */
1033n/a 0, /* tp_as_number */
1034n/a &mmap_as_sequence, /*tp_as_sequence*/
1035n/a &mmap_as_mapping, /*tp_as_mapping*/
1036n/a 0, /*tp_hash*/
1037n/a 0, /*tp_call*/
1038n/a 0, /*tp_str*/
1039n/a PyObject_GenericGetAttr, /*tp_getattro*/
1040n/a 0, /*tp_setattro*/
1041n/a &mmap_as_buffer, /*tp_as_buffer*/
1042n/a Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1043n/a mmap_doc, /*tp_doc*/
1044n/a 0, /* tp_traverse */
1045n/a 0, /* tp_clear */
1046n/a 0, /* tp_richcompare */
1047n/a offsetof(mmap_object, weakreflist), /* tp_weaklistoffset */
1048n/a 0, /* tp_iter */
1049n/a 0, /* tp_iternext */
1050n/a mmap_object_methods, /* tp_methods */
1051n/a 0, /* tp_members */
1052n/a mmap_object_getset, /* tp_getset */
1053n/a 0, /* tp_base */
1054n/a 0, /* tp_dict */
1055n/a 0, /* tp_descr_get */
1056n/a 0, /* tp_descr_set */
1057n/a 0, /* tp_dictoffset */
1058n/a 0, /* tp_init */
1059n/a PyType_GenericAlloc, /* tp_alloc */
1060n/a new_mmap_object, /* tp_new */
1061n/a PyObject_Del, /* tp_free */
1062n/a};
1063n/a
1064n/a
1065n/a#ifdef UNIX
1066n/a#ifdef HAVE_LARGEFILE_SUPPORT
1067n/a#define _Py_PARSE_OFF_T "L"
1068n/a#else
1069n/a#define _Py_PARSE_OFF_T "l"
1070n/a#endif
1071n/a
1072n/astatic PyObject *
1073n/anew_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1074n/a{
1075n/a struct _Py_stat_struct status;
1076n/a mmap_object *m_obj;
1077n/a Py_ssize_t map_size;
1078n/a off_t offset = 0;
1079n/a int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
1080n/a int devzero = -1;
1081n/a int access = (int)ACCESS_DEFAULT;
1082n/a static char *keywords[] = {"fileno", "length",
1083n/a "flags", "prot",
1084n/a "access", "offset", NULL};
1085n/a
1086n/a if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|iii" _Py_PARSE_OFF_T, keywords,
1087n/a &fd, &map_size, &flags, &prot,
1088n/a &access, &offset))
1089n/a return NULL;
1090n/a if (map_size < 0) {
1091n/a PyErr_SetString(PyExc_OverflowError,
1092n/a "memory mapped length must be postiive");
1093n/a return NULL;
1094n/a }
1095n/a if (offset < 0) {
1096n/a PyErr_SetString(PyExc_OverflowError,
1097n/a "memory mapped offset must be positive");
1098n/a return NULL;
1099n/a }
1100n/a
1101n/a if ((access != (int)ACCESS_DEFAULT) &&
1102n/a ((flags != MAP_SHARED) || (prot != (PROT_WRITE | PROT_READ))))
1103n/a return PyErr_Format(PyExc_ValueError,
1104n/a "mmap can't specify both access and flags, prot.");
1105n/a switch ((access_mode)access) {
1106n/a case ACCESS_READ:
1107n/a flags = MAP_SHARED;
1108n/a prot = PROT_READ;
1109n/a break;
1110n/a case ACCESS_WRITE:
1111n/a flags = MAP_SHARED;
1112n/a prot = PROT_READ | PROT_WRITE;
1113n/a break;
1114n/a case ACCESS_COPY:
1115n/a flags = MAP_PRIVATE;
1116n/a prot = PROT_READ | PROT_WRITE;
1117n/a break;
1118n/a case ACCESS_DEFAULT:
1119n/a /* map prot to access type */
1120n/a if ((prot & PROT_READ) && (prot & PROT_WRITE)) {
1121n/a /* ACCESS_DEFAULT */
1122n/a }
1123n/a else if (prot & PROT_WRITE) {
1124n/a access = ACCESS_WRITE;
1125n/a }
1126n/a else {
1127n/a access = ACCESS_READ;
1128n/a }
1129n/a break;
1130n/a default:
1131n/a return PyErr_Format(PyExc_ValueError,
1132n/a "mmap invalid access parameter.");
1133n/a }
1134n/a
1135n/a#ifdef __APPLE__
1136n/a /* Issue #11277: fsync(2) is not enough on OS X - a special, OS X specific
1137n/a fcntl(2) is necessary to force DISKSYNC and get around mmap(2) bug */
1138n/a if (fd != -1)
1139n/a (void)fcntl(fd, F_FULLFSYNC);
1140n/a#endif
1141n/a if (fd != -1 && _Py_fstat_noraise(fd, &status) == 0
1142n/a && S_ISREG(status.st_mode)) {
1143n/a if (map_size == 0) {
1144n/a if (status.st_size == 0) {
1145n/a PyErr_SetString(PyExc_ValueError,
1146n/a "cannot mmap an empty file");
1147n/a return NULL;
1148n/a }
1149n/a if (offset >= status.st_size) {
1150n/a PyErr_SetString(PyExc_ValueError,
1151n/a "mmap offset is greater than file size");
1152n/a return NULL;
1153n/a }
1154n/a if (status.st_size - offset > PY_SSIZE_T_MAX) {
1155n/a PyErr_SetString(PyExc_ValueError,
1156n/a "mmap length is too large");
1157n/a return NULL;
1158n/a }
1159n/a map_size = (Py_ssize_t) (status.st_size - offset);
1160n/a } else if (offset > status.st_size || status.st_size - offset < map_size) {
1161n/a PyErr_SetString(PyExc_ValueError,
1162n/a "mmap length is greater than file size");
1163n/a return NULL;
1164n/a }
1165n/a }
1166n/a m_obj = (mmap_object *)type->tp_alloc(type, 0);
1167n/a if (m_obj == NULL) {return NULL;}
1168n/a m_obj->data = NULL;
1169n/a m_obj->size = map_size;
1170n/a m_obj->pos = 0;
1171n/a m_obj->weakreflist = NULL;
1172n/a m_obj->exports = 0;
1173n/a m_obj->offset = offset;
1174n/a if (fd == -1) {
1175n/a m_obj->fd = -1;
1176n/a /* Assume the caller wants to map anonymous memory.
1177n/a This is the same behaviour as Windows. mmap.mmap(-1, size)
1178n/a on both Windows and Unix map anonymous memory.
1179n/a */
1180n/a#ifdef MAP_ANONYMOUS
1181n/a /* BSD way to map anonymous memory */
1182n/a flags |= MAP_ANONYMOUS;
1183n/a#else
1184n/a /* SVR4 method to map anonymous memory is to open /dev/zero */
1185n/a fd = devzero = _Py_open("/dev/zero", O_RDWR);
1186n/a if (devzero == -1) {
1187n/a Py_DECREF(m_obj);
1188n/a return NULL;
1189n/a }
1190n/a#endif
1191n/a }
1192n/a else {
1193n/a m_obj->fd = _Py_dup(fd);
1194n/a if (m_obj->fd == -1) {
1195n/a Py_DECREF(m_obj);
1196n/a return NULL;
1197n/a }
1198n/a }
1199n/a
1200n/a m_obj->data = mmap(NULL, map_size,
1201n/a prot, flags,
1202n/a fd, offset);
1203n/a
1204n/a if (devzero != -1) {
1205n/a close(devzero);
1206n/a }
1207n/a
1208n/a if (m_obj->data == (char *)-1) {
1209n/a m_obj->data = NULL;
1210n/a Py_DECREF(m_obj);
1211n/a PyErr_SetFromErrno(PyExc_OSError);
1212n/a return NULL;
1213n/a }
1214n/a m_obj->access = (access_mode)access;
1215n/a return (PyObject *)m_obj;
1216n/a}
1217n/a#endif /* UNIX */
1218n/a
1219n/a#ifdef MS_WINDOWS
1220n/a
1221n/a/* A note on sizes and offsets: while the actual map size must hold in a
1222n/a Py_ssize_t, both the total file size and the start offset can be longer
1223n/a than a Py_ssize_t, so we use long long which is always 64-bit.
1224n/a*/
1225n/a
1226n/astatic PyObject *
1227n/anew_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
1228n/a{
1229n/a mmap_object *m_obj;
1230n/a Py_ssize_t map_size;
1231n/a long long offset = 0, size;
1232n/a DWORD off_hi; /* upper 32 bits of offset */
1233n/a DWORD off_lo; /* lower 32 bits of offset */
1234n/a DWORD size_hi; /* upper 32 bits of size */
1235n/a DWORD size_lo; /* lower 32 bits of size */
1236n/a char *tagname = "";
1237n/a DWORD dwErr = 0;
1238n/a int fileno;
1239n/a HANDLE fh = 0;
1240n/a int access = (access_mode)ACCESS_DEFAULT;
1241n/a DWORD flProtect, dwDesiredAccess;
1242n/a static char *keywords[] = { "fileno", "length",
1243n/a "tagname",
1244n/a "access", "offset", NULL };
1245n/a
1246n/a if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1247n/a &fileno, &map_size,
1248n/a &tagname, &access, &offset)) {
1249n/a return NULL;
1250n/a }
1251n/a
1252n/a switch((access_mode)access) {
1253n/a case ACCESS_READ:
1254n/a flProtect = PAGE_READONLY;
1255n/a dwDesiredAccess = FILE_MAP_READ;
1256n/a break;
1257n/a case ACCESS_DEFAULT: case ACCESS_WRITE:
1258n/a flProtect = PAGE_READWRITE;
1259n/a dwDesiredAccess = FILE_MAP_WRITE;
1260n/a break;
1261n/a case ACCESS_COPY:
1262n/a flProtect = PAGE_WRITECOPY;
1263n/a dwDesiredAccess = FILE_MAP_COPY;
1264n/a break;
1265n/a default:
1266n/a return PyErr_Format(PyExc_ValueError,
1267n/a "mmap invalid access parameter.");
1268n/a }
1269n/a
1270n/a if (map_size < 0) {
1271n/a PyErr_SetString(PyExc_OverflowError,
1272n/a "memory mapped length must be postiive");
1273n/a return NULL;
1274n/a }
1275n/a if (offset < 0) {
1276n/a PyErr_SetString(PyExc_OverflowError,
1277n/a "memory mapped offset must be positive");
1278n/a return NULL;
1279n/a }
1280n/a
1281n/a /* assume -1 and 0 both mean invalid filedescriptor
1282n/a to 'anonymously' map memory.
1283n/a XXX: fileno == 0 is a valid fd, but was accepted prior to 2.5.
1284n/a XXX: Should this code be added?
1285n/a if (fileno == 0)
1286n/a PyErr_WarnEx(PyExc_DeprecationWarning,
1287n/a "don't use 0 for anonymous memory",
1288n/a 1);
1289n/a */
1290n/a if (fileno != -1 && fileno != 0) {
1291n/a /* Ensure that fileno is within the CRT's valid range */
1292n/a _Py_BEGIN_SUPPRESS_IPH
1293n/a fh = (HANDLE)_get_osfhandle(fileno);
1294n/a _Py_END_SUPPRESS_IPH
1295n/a if (fh==(HANDLE)-1) {
1296n/a PyErr_SetFromErrno(PyExc_OSError);
1297n/a return NULL;
1298n/a }
1299n/a /* Win9x appears to need us seeked to zero */
1300n/a lseek(fileno, 0, SEEK_SET);
1301n/a }
1302n/a
1303n/a m_obj = (mmap_object *)type->tp_alloc(type, 0);
1304n/a if (m_obj == NULL)
1305n/a return NULL;
1306n/a /* Set every field to an invalid marker, so we can safely
1307n/a destruct the object in the face of failure */
1308n/a m_obj->data = NULL;
1309n/a m_obj->file_handle = INVALID_HANDLE_VALUE;
1310n/a m_obj->map_handle = NULL;
1311n/a m_obj->tagname = NULL;
1312n/a m_obj->offset = offset;
1313n/a
1314n/a if (fh) {
1315n/a /* It is necessary to duplicate the handle, so the
1316n/a Python code can close it on us */
1317n/a if (!DuplicateHandle(
1318n/a GetCurrentProcess(), /* source process handle */
1319n/a fh, /* handle to be duplicated */
1320n/a GetCurrentProcess(), /* target proc handle */
1321n/a (LPHANDLE)&m_obj->file_handle, /* result */
1322n/a 0, /* access - ignored due to options value */
1323n/a FALSE, /* inherited by child processes? */
1324n/a DUPLICATE_SAME_ACCESS)) { /* options */
1325n/a dwErr = GetLastError();
1326n/a Py_DECREF(m_obj);
1327n/a PyErr_SetFromWindowsErr(dwErr);
1328n/a return NULL;
1329n/a }
1330n/a if (!map_size) {
1331n/a DWORD low,high;
1332n/a low = GetFileSize(fh, &high);
1333n/a /* low might just happen to have the value INVALID_FILE_SIZE;
1334n/a so we need to check the last error also. */
1335n/a if (low == INVALID_FILE_SIZE &&
1336n/a (dwErr = GetLastError()) != NO_ERROR) {
1337n/a Py_DECREF(m_obj);
1338n/a return PyErr_SetFromWindowsErr(dwErr);
1339n/a }
1340n/a
1341n/a size = (((long long) high) << 32) + low;
1342n/a if (size == 0) {
1343n/a PyErr_SetString(PyExc_ValueError,
1344n/a "cannot mmap an empty file");
1345n/a Py_DECREF(m_obj);
1346n/a return NULL;
1347n/a }
1348n/a if (offset >= size) {
1349n/a PyErr_SetString(PyExc_ValueError,
1350n/a "mmap offset is greater than file size");
1351n/a Py_DECREF(m_obj);
1352n/a return NULL;
1353n/a }
1354n/a if (size - offset > PY_SSIZE_T_MAX) {
1355n/a PyErr_SetString(PyExc_ValueError,
1356n/a "mmap length is too large");
1357n/a Py_DECREF(m_obj);
1358n/a return NULL;
1359n/a }
1360n/a m_obj->size = (Py_ssize_t) (size - offset);
1361n/a } else {
1362n/a m_obj->size = map_size;
1363n/a size = offset + map_size;
1364n/a }
1365n/a }
1366n/a else {
1367n/a m_obj->size = map_size;
1368n/a size = offset + map_size;
1369n/a }
1370n/a
1371n/a /* set the initial position */
1372n/a m_obj->pos = (size_t) 0;
1373n/a
1374n/a m_obj->weakreflist = NULL;
1375n/a m_obj->exports = 0;
1376n/a /* set the tag name */
1377n/a if (tagname != NULL && *tagname != '\0') {
1378n/a m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1379n/a if (m_obj->tagname == NULL) {
1380n/a PyErr_NoMemory();
1381n/a Py_DECREF(m_obj);
1382n/a return NULL;
1383n/a }
1384n/a strcpy(m_obj->tagname, tagname);
1385n/a }
1386n/a else
1387n/a m_obj->tagname = NULL;
1388n/a
1389n/a m_obj->access = (access_mode)access;
1390n/a size_hi = (DWORD)(size >> 32);
1391n/a size_lo = (DWORD)(size & 0xFFFFFFFF);
1392n/a off_hi = (DWORD)(offset >> 32);
1393n/a off_lo = (DWORD)(offset & 0xFFFFFFFF);
1394n/a /* For files, it would be sufficient to pass 0 as size.
1395n/a For anonymous maps, we have to pass the size explicitly. */
1396n/a m_obj->map_handle = CreateFileMapping(m_obj->file_handle,
1397n/a NULL,
1398n/a flProtect,
1399n/a size_hi,
1400n/a size_lo,
1401n/a m_obj->tagname);
1402n/a if (m_obj->map_handle != NULL) {
1403n/a m_obj->data = (char *) MapViewOfFile(m_obj->map_handle,
1404n/a dwDesiredAccess,
1405n/a off_hi,
1406n/a off_lo,
1407n/a m_obj->size);
1408n/a if (m_obj->data != NULL)
1409n/a return (PyObject *)m_obj;
1410n/a else {
1411n/a dwErr = GetLastError();
1412n/a CloseHandle(m_obj->map_handle);
1413n/a m_obj->map_handle = NULL;
1414n/a }
1415n/a } else
1416n/a dwErr = GetLastError();
1417n/a Py_DECREF(m_obj);
1418n/a PyErr_SetFromWindowsErr(dwErr);
1419n/a return NULL;
1420n/a}
1421n/a#endif /* MS_WINDOWS */
1422n/a
1423n/astatic void
1424n/asetint(PyObject *d, const char *name, long value)
1425n/a{
1426n/a PyObject *o = PyLong_FromLong(value);
1427n/a if (o && PyDict_SetItemString(d, name, o) == 0) {
1428n/a Py_DECREF(o);
1429n/a }
1430n/a}
1431n/a
1432n/a
1433n/astatic struct PyModuleDef mmapmodule = {
1434n/a PyModuleDef_HEAD_INIT,
1435n/a "mmap",
1436n/a NULL,
1437n/a -1,
1438n/a NULL,
1439n/a NULL,
1440n/a NULL,
1441n/a NULL,
1442n/a NULL
1443n/a};
1444n/a
1445n/aPyMODINIT_FUNC
1446n/aPyInit_mmap(void)
1447n/a{
1448n/a PyObject *dict, *module;
1449n/a
1450n/a if (PyType_Ready(&mmap_object_type) < 0)
1451n/a return NULL;
1452n/a
1453n/a module = PyModule_Create(&mmapmodule);
1454n/a if (module == NULL)
1455n/a return NULL;
1456n/a dict = PyModule_GetDict(module);
1457n/a if (!dict)
1458n/a return NULL;
1459n/a PyDict_SetItemString(dict, "error", PyExc_OSError);
1460n/a PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
1461n/a#ifdef PROT_EXEC
1462n/a setint(dict, "PROT_EXEC", PROT_EXEC);
1463n/a#endif
1464n/a#ifdef PROT_READ
1465n/a setint(dict, "PROT_READ", PROT_READ);
1466n/a#endif
1467n/a#ifdef PROT_WRITE
1468n/a setint(dict, "PROT_WRITE", PROT_WRITE);
1469n/a#endif
1470n/a
1471n/a#ifdef MAP_SHARED
1472n/a setint(dict, "MAP_SHARED", MAP_SHARED);
1473n/a#endif
1474n/a#ifdef MAP_PRIVATE
1475n/a setint(dict, "MAP_PRIVATE", MAP_PRIVATE);
1476n/a#endif
1477n/a#ifdef MAP_DENYWRITE
1478n/a setint(dict, "MAP_DENYWRITE", MAP_DENYWRITE);
1479n/a#endif
1480n/a#ifdef MAP_EXECUTABLE
1481n/a setint(dict, "MAP_EXECUTABLE", MAP_EXECUTABLE);
1482n/a#endif
1483n/a#ifdef MAP_ANONYMOUS
1484n/a setint(dict, "MAP_ANON", MAP_ANONYMOUS);
1485n/a setint(dict, "MAP_ANONYMOUS", MAP_ANONYMOUS);
1486n/a#endif
1487n/a
1488n/a setint(dict, "PAGESIZE", (long)my_getpagesize());
1489n/a
1490n/a setint(dict, "ALLOCATIONGRANULARITY", (long)my_getallocationgranularity());
1491n/a
1492n/a setint(dict, "ACCESS_READ", ACCESS_READ);
1493n/a setint(dict, "ACCESS_WRITE", ACCESS_WRITE);
1494n/a setint(dict, "ACCESS_COPY", ACCESS_COPY);
1495n/a return module;
1496n/a}