Index: Doc/lib/libmmap.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libmmap.tex,v retrieving revision 1.8 diff -c -r1.8 libmmap.tex *** Doc/lib/libmmap.tex 3 Dec 2001 18:27:22 -0000 1.8 --- Doc/lib/libmmap.tex 29 Mar 2003 21:10:36 -0000 *************** *** 39,45 **** \constant{ACCESS_COPY} memory map affects memory but does not update the underlying file. ! \begin{funcdesc}{mmap}{fileno, length\optional{, tagname\optional{, access}}} \strong{(Windows version)} Maps \var{length} bytes from the file specified by the file handle \var{fileno}, and returns a mmap object. If \var{length} is \code{0}, the maximum length of the map --- 39,46 ---- \constant{ACCESS_COPY} memory map affects memory but does not update the underlying file. ! \begin{funcdesc}{mmap}{fileno, length\optional{, tagname\optional{, ! access\optional{, offset}}}} \strong{(Windows version)} Maps \var{length} bytes from the file specified by the file handle \var{fileno}, and returns a mmap object. If \var{length} is \code{0}, the maximum length of the map *************** *** 54,63 **** mapping is created without a name. Avoiding the use of the tag parameter will assist in keeping your code portable between \UNIX{} and Windows. \end{funcdesc} \begin{funcdescni}{mmap}{fileno, length\optional{, flags\optional{, ! prot\optional{, access}}}} \strong{(\UNIX{} version)} Maps \var{length} bytes from the file specified by the file descriptor \var{fileno}, and returns a mmap object. --- 55,69 ---- mapping is created without a name. Avoiding the use of the tag parameter will assist in keeping your code portable between \UNIX{} and Windows. + + \var{offset} may be specified as a non-negative integer offset. + mmap references will be relative to the offset from the beginning + of the file. + \versionchanged[The offset parameter was added]{2.3} \end{funcdesc} \begin{funcdescni}{mmap}{fileno, length\optional{, flags\optional{, ! prot\optional{, access\optional{, offset}}}}} \strong{(\UNIX{} version)} Maps \var{length} bytes from the file specified by the file descriptor \var{fileno}, and returns a mmap object. *************** *** 78,83 **** --- 84,94 ---- as an optional keyword parameter. It is an error to specify both \var{flags}, \var{prot} and \var{access}. See the description of \var{access} above for information on how to use this parameter. + + \var{offset} may be specified as a non-negative integer offset. + mmap references will be relative to the offset from the beginning + of the file. + \versionchanged[The offset parameter was added]{2.3} \end{funcdescni} Index: Lib/test/test_mmap.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_mmap.py,v retrieving revision 1.30 diff -c -r1.30 test_mmap.py *** Lib/test/test_mmap.py 13 Jan 2003 21:38:45 -0000 1.30 --- Lib/test/test_mmap.py 29 Mar 2003 21:10:36 -0000 *************** *** 4,9 **** --- 4,50 ---- PAGESIZE = mmap.PAGESIZE + def make_mmap_file(f): + # Write 2 pages worth of data to the file + f.write('\0'* PAGESIZE) + f.write('foo') + f.write('\0'* (PAGESIZE-3) ) + f.flush() + return mmap.mmap(f.fileno(), 2 * PAGESIZE) + + def test_offset(): + f = open(TESTFN, 'w+') + + try: # unlink TESTFN no matter what + m = make_mmap_file(f) + m.close() + f.close() + + mapsize = PAGESIZE * 2 + # Try invalid offset + f = open(TESTFN, "r+b") + for offset in [-2, -1, None]: + try: + m = mmap.mmap(f.fileno(), mapsize, offset=offset) + except (ValueError, TypeError): + pass + else: + verify(0, "Invalid offset should raise ValueError.") + f.close() + + # Try valid offset, hopefully 8192 works on all OSes + f = open(TESTFN, "r+b") + m = mmap.mmap(f.fileno(), mapsize - PAGESIZE, offset=PAGESIZE) + verify(m[0:3] == 'foo', "Invalid data found using offset.") + f.close() + + finally: + f.close() + try: + os.unlink(TESTFN) + except OSError: + pass + def test_both(): "Test mmap module on Unix systems and Windows" *************** *** 13,24 **** f = open(TESTFN, 'w+') try: # unlink TESTFN no matter what ! # Write 2 pages worth of data to the file ! f.write('\0'* PAGESIZE) ! f.write('foo') ! f.write('\0'* (PAGESIZE-3) ) ! f.flush() ! m = mmap.mmap(f.fileno(), 2 * PAGESIZE) f.close() # Simple sanity checks --- 54,60 ---- f = open(TESTFN, 'w+') try: # unlink TESTFN no matter what ! m = make_mmap_file(f) f.close() # Simple sanity checks *************** *** 311,317 **** finally: os.unlink(TESTFN) - print ' Test passed' ! test_both() --- 347,357 ---- finally: os.unlink(TESTFN) print ' Test passed' ! def test_main(): ! test_offset() ! test_both() ! ! if __name__ == '__main__': ! test_main() Index: Modules/mmapmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/mmapmodule.c,v retrieving revision 2.43 diff -c -r2.43 mmapmodule.c *** Modules/mmapmodule.c 7 Feb 2003 19:44:56 -0000 2.43 --- Modules/mmapmodule.c 29 Mar 2003 21:10:36 -0000 *************** *** 3,8 **** --- 3,11 ---- / Hacked for Unix by AMK / $Id: mmapmodule.c,v 2.43 2003/02/07 19:44:56 nnorwitz Exp $ + / Modified to support mmap with offset - to map a 'window' of a file + / Author: Yotam Medini yotamm@mellanox.co.il + / / mmapmodule.cpp -- map a view of a file into memory / / todo: need permission flags, perhaps a 'chsize' analog *************** *** 73,79 **** PyObject_HEAD char * data; size_t size; ! size_t pos; #ifdef MS_WINDOWS HANDLE map_handle; --- 76,83 ---- PyObject_HEAD char * data; size_t size; ! size_t pos; /* relative to offset */ ! size_t offset; /* always 0 for MS_WINDOWS */ #ifdef MS_WINDOWS HANDLE map_handle; *************** *** 261,267 **** if (i == len) { return Py_BuildValue ( "l", ! (long) (p - self->data)); } } return Py_BuildValue ("l", (long) -1); --- 265,271 ---- if (i == len) { return Py_BuildValue ( "l", ! (long)(self->offset + (p - self->data))); } } return Py_BuildValue ("l", (long) -1); *************** *** 453,459 **** CHECK_VALID(NULL); if (!PyArg_ParseTuple(args, ":tell")) return NULL; ! return (Py_BuildValue ("l", (long) self->pos) ); } static PyObject * --- 457,463 ---- CHECK_VALID(NULL); if (!PyArg_ParseTuple(args, ":tell")) return NULL; ! return (Py_BuildValue ("l", (long) (self->offset + self->pos)) ); } static PyObject * *************** *** 499,504 **** --- 503,509 ---- size_t where; switch (how) { case 0: /* relative to start */ + dist -= self->offset; if (dist < 0) goto onoutofrange; where = dist; *************** *** 539,544 **** --- 544,551 ---- !is_writeable(self)) { return NULL; } else { + dest -= self->offset; + src -= self->offset; /* bounds check the values */ if (/* end of source after end of data?? */ ((src+count) > self->size) *************** *** 857,875 **** #endif mmap_object *m_obj; PyObject *map_size_obj = NULL; ! int map_size; int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ; access_mode access = ACCESS_DEFAULT; char *keywords[] = {"fileno", "length", "flags", "prot", ! "access", NULL}; ! if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords, ! &fd, &map_size_obj, &flags, &prot, &access)) return NULL; map_size = _GetMapSize(map_size_obj); if (map_size < 0) return NULL; if ((access != ACCESS_DEFAULT) && ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ)))) --- 864,891 ---- #endif mmap_object *m_obj; PyObject *map_size_obj = NULL; ! int map_size, int_offset = 0; int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ; access_mode access = ACCESS_DEFAULT; + off_t offset; char *keywords[] = {"fileno", "length", "flags", "prot", ! "access", "offset", NULL}; ! if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iiii", keywords, ! &fd, &map_size_obj, &flags, &prot, ! &access, &int_offset)) return NULL; map_size = _GetMapSize(map_size_obj); if (map_size < 0) return NULL; + if (int_offset < 0) + { + PyErr_SetString(PyExc_ValueError, + "offset must be a non-negative integer"); + return NULL; + } + offset = (off_t) int_offset; if ((access != ACCESS_DEFAULT) && ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ)))) *************** *** 897,903 **** } #ifdef HAVE_FSTAT ! if (fstat(fd, &st) == 0 && (size_t)map_size > st.st_size) { PyErr_SetString(PyExc_ValueError, "mmap length is greater than file size"); return NULL; --- 913,922 ---- } #ifdef HAVE_FSTAT ! if (fstat(fd, &st) == 0 && ! !S_ISCHR(st.st_mode) && ! offset + (size_t)map_size > st.st_size) ! { PyErr_SetString(PyExc_ValueError, "mmap length is greater than file size"); return NULL; *************** *** 907,916 **** if (m_obj == NULL) {return NULL;} m_obj->size = (size_t) map_size; m_obj->pos = (size_t) 0; m_obj->fd = fd; m_obj->data = mmap(NULL, map_size, prot, flags, ! fd, 0); if (m_obj->data == (char *)-1) { Py_DECREF(m_obj); PyErr_SetFromErrno(mmap_module_error); --- 926,936 ---- if (m_obj == NULL) {return NULL;} m_obj->size = (size_t) map_size; m_obj->pos = (size_t) 0; + m_obj->offset = offset; m_obj->fd = fd; m_obj->data = mmap(NULL, map_size, prot, flags, ! fd, offset); if (m_obj->data == (char *)-1) { Py_DECREF(m_obj); PyErr_SetFromErrno(mmap_module_error); *************** *** 927,949 **** { mmap_object *m_obj; PyObject *map_size_obj = NULL; ! int map_size; char *tagname = ""; DWORD dwErr = 0; int fileno; HANDLE fh = 0; access_mode access = ACCESS_DEFAULT; ! DWORD flProtect, dwDesiredAccess; char *keywords[] = { "fileno", "length", "tagname", ! "access", NULL }; ! if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords, &fileno, &map_size_obj, ! &tagname, &access)) { return NULL; } switch(access) { case ACCESS_READ: flProtect = PAGE_READONLY; --- 947,977 ---- { mmap_object *m_obj; PyObject *map_size_obj = NULL; ! int map_size, int_offset = 0; char *tagname = ""; DWORD dwErr = 0; int fileno; HANDLE fh = 0; access_mode access = ACCESS_DEFAULT; ! DWORD flProtect, dwDesiredAccess, offset; char *keywords[] = { "fileno", "length", "tagname", ! "access", "offset", NULL }; ! if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zii", keywords, &fileno, &map_size_obj, ! &tagname, &access, &int_offset)) { return NULL; } + if (int_offset < 0) + { + PyErr_SetString(PyExc_ValueError, + "offset must be a non-negative integer"); + return NULL; + } + offset = (DWORD) int_offset; + switch(access) { case ACCESS_READ: flProtect = PAGE_READONLY; *************** *** 986,991 **** --- 1014,1020 ---- m_obj->file_handle = INVALID_HANDLE_VALUE; m_obj->map_handle = INVALID_HANDLE_VALUE; m_obj->tagname = NULL; + m_obj->offset = offset; if (fh) { /* It is necessary to duplicate the handle, so the *************** *** 1040,1046 **** m_obj->data = (char *) MapViewOfFile (m_obj->map_handle, dwDesiredAccess, 0, ! 0, 0); if (m_obj->data != NULL) { return ((PyObject *) m_obj); --- 1069,1075 ---- m_obj->data = (char *) MapViewOfFile (m_obj->map_handle, dwDesiredAccess, 0, ! offset, 0); if (m_obj->data != NULL) { return ((PyObject *) m_obj);