mmapmodule.c 22.7 KB
Newer Older
1 2
/*
 /  Author: Sam Rushing <rushing@nightmare.com>
3
 /  Hacked for Unix by A.M. Kuchling <amk1@bigfoot.com> 
4
 /  $Id$
5 6 7 8 9 10 11 12 13 14

 / mmapmodule.cpp -- map a view of a file into memory
 /
 / todo: need permission flags, perhaps a 'chsize' analog
 /   not all functions check range yet!!!
 /
 /
 / Note: This module currently only deals with 32-bit file
 /   sizes.
 /
Mark Hammond's avatar
Mark Hammond committed
15 16 17
 / This version of mmapmodule.c has been changed significantly
 / from the original mmapfile.c on which it was based.
 / The original version of mmapfile is maintained by Sam at
18 19 20
 / ftp://squirl.nightmare.com/pub/python/python-ext.
*/

21 22
#include <Python.h>

23 24 25 26 27 28
#ifndef MS_WIN32
#define UNIX
#endif

#ifdef MS_WIN32
#include <windows.h>
29 30 31 32 33 34 35
static int
my_getpagesize(void)
{
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    return si.dwPageSize;
}
36 37 38 39 40
#endif

#ifdef UNIX
#include <unistd.h>
#include <sys/mman.h>
41
#include <sys/stat.h>
42 43 44 45

#ifndef MS_SYNC
/* This is missing e.g. on SunOS 4.1.4 */
#define MS_SYNC 0
46 47
#endif

48 49 50 51 52 53 54 55 56 57
#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
static int
my_getpagesize(void)
{
    return sysconf(_SC_PAGESIZE);
}
#else
#define my_getpagesize getpagesize
#endif

58 59
#endif /* UNIX */

60 61 62 63 64 65
#include <string.h>
#include <sys/types.h>

static PyObject *mmap_module_error;

typedef struct {
66 67 68 69
	PyObject_HEAD
	char *	data;
	size_t	size;
	size_t	pos;
70 71

#ifdef MS_WIN32
72
	HANDLE	map_handle;
Mark Hammond's avatar
Mark Hammond committed
73
	HANDLE	file_handle;
74
	char *	tagname;
75 76 77
#endif

#ifdef UNIX
78
        int fd;
79 80 81 82
#endif
} mmap_object;

static void
Fredrik Lundh's avatar
Fredrik Lundh committed
83
mmap_object_dealloc(mmap_object *m_obj)
84 85
{
#ifdef MS_WIN32
86 87 88 89
	if (m_obj->data != NULL)
		UnmapViewOfFile (m_obj->data);
	if (m_obj->map_handle != INVALID_HANDLE_VALUE)
		CloseHandle (m_obj->map_handle);
Mark Hammond's avatar
Mark Hammond committed
90 91
	if (m_obj->file_handle != INVALID_HANDLE_VALUE)
		CloseHandle (m_obj->file_handle);
92 93
	if (m_obj->tagname)
		PyMem_Free(m_obj->tagname);
94 95 96
#endif /* MS_WIN32 */

#ifdef UNIX
97
	if (m_obj->data!=NULL) {
98
		msync(m_obj->data, m_obj->size, MS_SYNC);
99 100
		munmap(m_obj->data, m_obj->size);
	}
101 102
#endif /* UNIX */

103
	PyObject_Del(m_obj);
104 105 106
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
107
mmap_close_method(mmap_object *self, PyObject *args)
108
{
109
        if (!PyArg_ParseTuple(args, ":close"))
110
		return NULL;
111
#ifdef MS_WIN32
Mark Hammond's avatar
Mark Hammond committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
	/* For each resource we maintain, we need to check
	   the value is valid, and if so, free the resource 
	   and set the member value to an invalid value so
	   the dealloc does not attempt to resource clearing
	   again.
	   TODO - should we check for errors in the close operations???
	*/
	if (self->data != NULL) {
		UnmapViewOfFile (self->data);
		self->data = NULL;
	}
	if (self->map_handle != INVALID_HANDLE_VALUE) {
		CloseHandle (self->map_handle);
		self->map_handle = INVALID_HANDLE_VALUE;
	}
	if (self->file_handle != INVALID_HANDLE_VALUE) {
		CloseHandle (self->file_handle);
		self->file_handle = INVALID_HANDLE_VALUE;
	}
131 132 133
#endif /* MS_WIN32 */

#ifdef UNIX
134 135
	munmap(self->data, self->size);
	self->data = NULL;
136 137
#endif

138 139
	Py_INCREF (Py_None);
	return (Py_None);
140 141 142
}

#ifdef MS_WIN32
143 144 145 146 147 148
#define CHECK_VALID(err)						\
do {									\
    if (!self->map_handle) {						\
	PyErr_SetString (PyExc_ValueError, "mmap closed or invalid");	\
	return err;							\
    }									\
149 150 151 152
} while (0)
#endif /* MS_WIN32 */

#ifdef UNIX
153 154 155 156 157 158
#define CHECK_VALID(err)						\
do {									\
    if (self->data == NULL) {						\
	PyErr_SetString (PyExc_ValueError, "mmap closed or invalid");	\
	return err;							\
	}								\
159 160 161 162
} while (0)
#endif /* UNIX */

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
163 164
mmap_read_byte_method(mmap_object *self,
		      PyObject *args)
165
{
166
	char value;
Fredrik Lundh's avatar
Fredrik Lundh committed
167
	char *where;
168
	CHECK_VALID(NULL);
169
        if (!PyArg_ParseTuple(args, ":read_byte"))
170
		return NULL;
171
	if (self->pos < self->size) {
172
	        where = self->data + self->pos;
173 174 175 176 177 178 179
		value = (char) *(where);
		self->pos += 1;
		return Py_BuildValue("c", (char) *(where));
	} else {
		PyErr_SetString (PyExc_ValueError, "read byte out of range");
		return NULL;
	}
180 181 182
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
183 184
mmap_read_line_method(mmap_object *self,
		     PyObject *args)
185
{
Fredrik Lundh's avatar
Fredrik Lundh committed
186 187 188 189
	char *start = self->data+self->pos;
	char *eof = self->data+self->size;
	char *eol;
	PyObject *result;
190 191

	CHECK_VALID(NULL);
192
        if (!PyArg_ParseTuple(args, ":readline"))
193
		return NULL;
194

195 196 197 198 199 200
	eol = memchr(start, '\n', self->size - self->pos);
	if (!eol)
		eol = eof;
	else
		++eol;		/* we're interested in the position after the
				   newline. */
201
	result = PyString_FromStringAndSize(start, (eol - start));
202 203
	self->pos += (eol - start);
	return (result);
204 205 206
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
207 208
mmap_read_method(mmap_object *self,
		 PyObject *args)
209
{
210 211 212 213
	long num_bytes;
	PyObject *result;

	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
214
	if (!PyArg_ParseTuple(args, "l:read", &num_bytes))
215 216 217 218 219 220 221 222 223
		return(NULL);

	/* silently 'adjust' out-of-range requests */
	if ((self->pos + num_bytes) > self->size) {
		num_bytes -= (self->pos+num_bytes) - self->size;
	}
	result = Py_BuildValue("s#", self->data+self->pos, num_bytes);
	self->pos += num_bytes;	 
	return (result);
224 225 226
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
227 228
mmap_find_method(mmap_object *self,
		 PyObject *args)
229
{
230
	int start = self->pos;
Fredrik Lundh's avatar
Fredrik Lundh committed
231
	char *needle;
232
	int len;
233

234
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
235
	if (!PyArg_ParseTuple (args, "s#|i:find", &needle, &len, &start)) {
236 237
		return NULL;
	} else {
Fredrik Lundh's avatar
Fredrik Lundh committed
238 239
		char *p = self->data+self->pos;
		char *e = self->data+self->size;
240
		while (p < e) {
Fredrik Lundh's avatar
Fredrik Lundh committed
241 242
			char *s = p;
			char *n = needle;
243 244 245 246 247
			while ((s<e) && (*n) && !(*s-*n)) {
				s++, n++;
			}
			if (!*n) {
				return Py_BuildValue (
248 249
					"i",
					(int) (p - (self->data + start)));
250 251
			}
			p++;
252
		}
253 254
		return Py_BuildValue ("l", (long) -1);
	}
255 256 257
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
258 259
mmap_write_method(mmap_object *self,
		  PyObject *args)
260
{
261
	long length;
Fredrik Lundh's avatar
Fredrik Lundh committed
262
	char *data;
263

264
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
265
	if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
266
		return(NULL);
267

268 269 270 271 272 273 274 275
	if ((self->pos + length) > self->size) {
		PyErr_SetString (PyExc_ValueError, "data out of range");
		return NULL;
	}
	memcpy (self->data+self->pos, data, length);
	self->pos = self->pos+length;
	Py_INCREF (Py_None);
	return (Py_None);
276 277 278
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
279 280
mmap_write_byte_method(mmap_object *self,
		       PyObject *args)
281
{
282
	char value;
283

284
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
285
	if (!PyArg_ParseTuple (args, "c:write_byte", &value))
286
		return(NULL);
287

288 289 290 291
	*(self->data+self->pos) = value;
	self->pos += 1;
	Py_INCREF (Py_None);
	return (Py_None);
292 293 294
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
295 296
mmap_size_method(mmap_object *self,
		 PyObject *args)
297
{
298
	CHECK_VALID(NULL);
299
        if (!PyArg_ParseTuple(args, ":size"))
300
		return NULL;
301 302

#ifdef MS_WIN32
Mark Hammond's avatar
Mark Hammond committed
303
	if (self->file_handle != INVALID_HANDLE_VALUE) {
304
		return (Py_BuildValue (
305
			"l", (long)
Mark Hammond's avatar
Mark Hammond committed
306
			GetFileSize (self->file_handle, NULL)));
307
	} else {
308
		return (Py_BuildValue ("l", (long) self->size) );
309
	}
310 311 312
#endif /* MS_WIN32 */

#ifdef UNIX
313 314 315 316 317 318
	{
		struct stat buf;
		if (-1 == fstat(self->fd, &buf)) {
			PyErr_SetFromErrno(mmap_module_error);
			return NULL;
		}
319
		return (Py_BuildValue ("l", (long) buf.st_size) );
320
	}
321 322 323 324 325 326 327 328 329 330 331 332 333
#endif /* UNIX */
}

/* This assumes that you want the entire file mapped,
 / and when recreating the map will make the new file
 / have the new size
 /
 / Is this really necessary?  This could easily be done
 / from python by just closing and re-opening with the
 / new size?
 */

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
334 335
mmap_resize_method(mmap_object *self,
		   PyObject *args)
336
{
337 338
	unsigned long new_size;
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
339
	if (!PyArg_ParseTuple (args, "l:resize", &new_size)) {
340
		return NULL;
341
#ifdef MS_WIN32
342 343 344 345 346
	} else { 
		DWORD dwErrCode = 0;
		/* First, unmap the file view */
		UnmapViewOfFile (self->data);
		/* Close the mapping object */
Mark Hammond's avatar
Mark Hammond committed
347
		CloseHandle (self->map_handle);
348
		/* Move to the desired EOF position */
Mark Hammond's avatar
Mark Hammond committed
349
		SetFilePointer (self->file_handle,
350 351
				new_size, NULL, FILE_BEGIN);
		/* Change the size of the file */
Mark Hammond's avatar
Mark Hammond committed
352
		SetEndOfFile (self->file_handle);
353 354
		/* Create another mapping object and remap the file view */
		self->map_handle = CreateFileMapping (
Mark Hammond's avatar
Mark Hammond committed
355
			self->file_handle,
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
			NULL,
			PAGE_READWRITE,
			0,
			new_size,
			self->tagname);
		if (self->map_handle != NULL) {
			self->data = (char *) MapViewOfFile (self->map_handle,
							     FILE_MAP_WRITE,
							     0,
							     0,
							     0);
			if (self->data != NULL) {
				self->size = new_size;
				Py_INCREF (Py_None);
				return Py_None;
			} else {
				dwErrCode = GetLastError();
			}
		} else {
			dwErrCode = GetLastError();
376
		}
377 378
		PyErr_SetFromWindowsErr(dwErrCode);
		return (NULL);
379 380 381
#endif /* MS_WIN32 */

#ifdef UNIX
382
#ifndef HAVE_MREMAP 
383
} else {
384 385 386
	PyErr_SetString(PyExc_SystemError,
			"mmap: resizing not available--no mremap()");
	return NULL;
387 388
#else
} else {
389
	void *newmap;
390

391
#ifdef MREMAP_MAYMOVE
392
	newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
393 394 395
#else
	newmap = mremap(self->data, self->size, new_size, 0);
#endif
396 397 398 399 400 401 402 403 404
	if (newmap == (void *)-1) 
	{
		PyErr_SetFromErrno(mmap_module_error);
		return NULL;
	}
	self->data = newmap;
	self->size = new_size;
	Py_INCREF(Py_None);
	return Py_None;
405
#endif /* HAVE_MREMAP */
406 407 408 409 410
#endif /* UNIX */
}
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
411
mmap_tell_method(mmap_object *self, PyObject *args)
412
{
413
	CHECK_VALID(NULL);
414
        if (!PyArg_ParseTuple(args, ":tell"))
415
		return NULL;
416
	return (Py_BuildValue ("l", (long) self->pos) );
417 418 419
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
420
mmap_flush_method(mmap_object *self, PyObject *args)
421
{
422 423 424
	size_t offset	= 0;
	size_t size = self->size;
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
425
	if (!PyArg_ParseTuple (args, "|ll:flush", &offset, &size)) {
426 427 428 429 430 431
		return NULL;
	} else if ((offset + size) > self->size) {
		PyErr_SetString (PyExc_ValueError,
				 "flush values out of range");
		return NULL;
	} else {
432
#ifdef MS_WIN32
433 434
		return (Py_BuildValue("l", (long)
                                      FlushViewOfFile(self->data+offset, size)));
435 436
#endif /* MS_WIN32 */
#ifdef UNIX
437 438 439
		/* XXX semantics of return value? */
		/* XXX flags for msync? */
		if (-1 == msync(self->data + offset, size,
440
				MS_SYNC))
441 442 443
		{
			PyErr_SetFromErrno(mmap_module_error);
			return NULL;
444
		}
445
		return Py_BuildValue ("l", (long) 0);	
446 447
#endif /* UNIX */   
	}
448 449 450
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
451
mmap_seek_method(mmap_object *self, PyObject *args)
452
{
453
	int dist;
454 455
	int how=0;
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
456
	if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) {
457 458
		return(NULL);
	} else {
459
		size_t where;
460
		switch (how) {
461 462 463
		case 0: /* relative to start */
			if (dist < 0)
				goto onoutofrange;
464 465
			where = dist;
			break;
466 467 468
		case 1: /* relative to current position */
			if ((int)self->pos + dist < 0)
				goto onoutofrange;
469 470
			where = self->pos + dist;
			break;
471 472 473 474
		case 2: /* relative to end */
			if ((int)self->size + dist < 0)
				goto onoutofrange;
			where = self->size + dist;
475 476 477 478 479 480
			break;
		default:
			PyErr_SetString (PyExc_ValueError,
					 "unknown seek type");
			return NULL;
		}
481 482 483 484 485
		if (where > self->size)
			goto onoutofrange;
		self->pos = where;
		Py_INCREF (Py_None);
		return (Py_None);
486
	}
487 488 489 490

onoutofrange:
	PyErr_SetString (PyExc_ValueError, "seek out of range");
	return NULL;
491 492 493
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
494
mmap_move_method(mmap_object *self, PyObject *args)
495
{
496 497
	unsigned long dest, src, count;
	CHECK_VALID(NULL);
Fredrik Lundh's avatar
Fredrik Lundh committed
498
	if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count)) {
499 500 501 502 503 504 505 506 507 508
		return NULL;
	} else {
		/* bounds check the values */
		if (/* end of source after end of data?? */
			((src+count) > self->size)
			/* dest will fit? */
			|| (dest+count > self->size)) {
			PyErr_SetString (PyExc_ValueError,
					 "source or destination out of range");
			return NULL;
509
		} else {
510 511 512
			memmove (self->data+dest, self->data+src, count);
			Py_INCREF (Py_None);
			return Py_None;
513
		}
514
	}
515 516 517
}

static struct PyMethodDef mmap_object_methods[] = {
518 519 520 521 522 523 524 525 526 527 528 529 530 531
	{"close",	(PyCFunction) mmap_close_method,	1},
	{"find",	(PyCFunction) mmap_find_method,		1},
	{"flush",	(PyCFunction) mmap_flush_method,	1},
	{"move",	(PyCFunction) mmap_move_method,		1},
	{"read",	(PyCFunction) mmap_read_method,		1},
	{"read_byte",	(PyCFunction) mmap_read_byte_method,  	1},
	{"readline",	(PyCFunction) mmap_read_line_method,	1},
	{"resize",	(PyCFunction) mmap_resize_method,	1},
	{"seek",	(PyCFunction) mmap_seek_method,		1},
	{"size",	(PyCFunction) mmap_size_method,		1},
	{"tell",	(PyCFunction) mmap_tell_method,		1},
	{"write",	(PyCFunction) mmap_write_method,	1},
	{"write_byte",	(PyCFunction) mmap_write_byte_method,	1},
	{NULL,	   NULL}       /* sentinel */
532 533 534 535 536
};

/* Functions for treating an mmap'ed file as a buffer */

static int
537
mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr)
538
{
539 540 541 542 543 544 545 546
	CHECK_VALID(-1);
	if ( index != 0 ) {
		PyErr_SetString(PyExc_SystemError,
				"Accessing non-existent mmap segment");
		return -1;
	}
	*ptr = self->data;
	return self->size;
547 548 549
}

static int
Fredrik Lundh's avatar
Fredrik Lundh committed
550
mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr)
551
{  
552 553 554 555 556 557 558 559
	CHECK_VALID(-1);
	if ( index != 0 ) {
		PyErr_SetString(PyExc_SystemError,
				"Accessing non-existent mmap segment");
		return -1;
	}
	*ptr = self->data;
	return self->size;
560 561 562
}

static int
Fredrik Lundh's avatar
Fredrik Lundh committed
563
mmap_buffer_getsegcount(mmap_object *self, int *lenp)
564
{
565 566 567 568
	CHECK_VALID(-1);
	if (lenp) 
		*lenp = self->size;
	return 1;
569 570 571
}

static int
Fredrik Lundh's avatar
Fredrik Lundh committed
572
mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr)
573
{
574 575 576 577 578 579 580
	if ( index != 0 ) {
		PyErr_SetString(PyExc_SystemError,
				"accessing non-existent buffer segment");
		return -1;
	}
	*ptr = (const char *)self->data;
	return self->size;
581 582 583
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
584
mmap_object_getattr(mmap_object *self, char *name)
585
{
586
	return Py_FindMethod (mmap_object_methods, (PyObject *)self, name);
587 588 589
}

static int
Fredrik Lundh's avatar
Fredrik Lundh committed
590
mmap_length(mmap_object *self)
591
{
592 593
	CHECK_VALID(-1);
	return self->size;
594 595 596
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
597
mmap_item(mmap_object *self, int i)
598
{
599
	CHECK_VALID(NULL);
600
	if (i < 0 || (size_t)i >= self->size) {
601 602 603 604
		PyErr_SetString(PyExc_IndexError, "mmap index out of range");
		return NULL;
	}
	return PyString_FromStringAndSize(self->data + i, 1);
605 606 607
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
608
mmap_slice(mmap_object *self, int ilow, int ihigh)
609
{
610 611 612
	CHECK_VALID(NULL);
	if (ilow < 0)
		ilow = 0;
613
	else if ((size_t)ilow > self->size)
614 615 616 617 618
		ilow = self->size;
	if (ihigh < 0)
		ihigh = 0;
	if (ihigh < ilow)
		ihigh = ilow;
619
	else if ((size_t)ihigh > self->size)
620 621 622
		ihigh = self->size;
    
	return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow);
623 624 625
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
626
mmap_concat(mmap_object *self, PyObject *bb)
627
{
628 629 630 631
	CHECK_VALID(NULL);
	PyErr_SetString(PyExc_SystemError,
			"mmaps don't support concatenation");
	return NULL;
632 633 634
}

static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
635
mmap_repeat(mmap_object *self, int n)
636
{
637 638 639 640
	CHECK_VALID(NULL);
	PyErr_SetString(PyExc_SystemError,
			"mmaps don't support repeat operation");
	return NULL;
641 642 643
}

static int
Fredrik Lundh's avatar
Fredrik Lundh committed
644
mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v)
645
{
646
	const char *buf;
647 648 649 650

	CHECK_VALID(-1);
	if (ilow < 0)
		ilow = 0;
651
	else if ((size_t)ilow > self->size)
652 653 654 655 656
		ilow = self->size;
	if (ihigh < 0)
		ihigh = 0;
	if (ihigh < ilow)
		ihigh = ilow;
657
	else if ((size_t)ihigh > self->size)
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
		ihigh = self->size;
    
	if (! (PyString_Check(v)) ) {
		PyErr_SetString(PyExc_IndexError, 
				"mmap slice assignment must be a string");
		return -1;
	}
	if ( PyString_Size(v) != (ihigh - ilow) ) {
		PyErr_SetString(PyExc_IndexError, 
				"mmap slice assignment is wrong size");
		return -1;
	}
	buf = PyString_AsString(v);
	memcpy(self->data + ilow, buf, ihigh-ilow);
	return 0;
673 674 675
}

static int
Fredrik Lundh's avatar
Fredrik Lundh committed
676
mmap_ass_item(mmap_object *self, int i, PyObject *v)
677
{
678
	const char *buf;
679
 
680
	CHECK_VALID(-1);
681
	if (i < 0 || (size_t)i >= self->size) {
682 683 684 685 686 687 688 689 690 691 692
		PyErr_SetString(PyExc_IndexError, "mmap index out of range");
		return -1;
	}
	if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
		PyErr_SetString(PyExc_IndexError, 
			"mmap assignment must be single-character string");
		return -1;
	}
	buf = PyString_AsString(v);
	self->data[i] = buf[0];
	return 0;
693 694 695
}

static PySequenceMethods mmap_as_sequence = {
696 697 698 699 700 701 702
	(inquiry)mmap_length,		       /*sq_length*/
	(binaryfunc)mmap_concat,	       /*sq_concat*/
	(intargfunc)mmap_repeat,	       /*sq_repeat*/
	(intargfunc)mmap_item,		       /*sq_item*/
	(intintargfunc)mmap_slice,	       /*sq_slice*/
	(intobjargproc)mmap_ass_item,	       /*sq_ass_item*/
	(intintobjargproc)mmap_ass_slice,      /*sq_ass_slice*/
703 704 705
};

static PyBufferProcs mmap_as_buffer = {
706 707 708 709
	(getreadbufferproc)mmap_buffer_getreadbuf,
	(getwritebufferproc)mmap_buffer_getwritebuf,
	(getsegcountproc)mmap_buffer_getsegcount,
	(getcharbufferproc)mmap_buffer_getcharbuffer,
710 711 712
};

static PyTypeObject mmap_object_type = {
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
	PyObject_HEAD_INIT(0) /* patched in module init */
	0,					/* ob_size */
	"mmap",					/* tp_name */
	sizeof(mmap_object),			/* tp_size */
	0,					/* tp_itemsize */
	/* methods */
	(destructor) mmap_object_dealloc,	/* tp_dealloc */
	0,					/* tp_print */
	(getattrfunc) mmap_object_getattr,	/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	0,					/* tp_repr */
	0,					/* tp_as_number */
	&mmap_as_sequence,			/*tp_as_sequence*/
	0,					/*tp_as_mapping*/
	0,					/*tp_hash*/
	0,					/*tp_call*/
	0,					/*tp_str*/
	0,					/*tp_getattro*/
	0,					/*tp_setattro*/
	&mmap_as_buffer,			/*tp_as_buffer*/
	Py_TPFLAGS_HAVE_GETCHARBUFFER,		/*tp_flags*/
	0,					/*tp_doc*/
736 737
};

738 739 740 741 742 743 744 745

/* extract the map size from the given PyObject

   The map size is restricted to [0, INT_MAX] because this is the current
   Python limitation on object sizes. Although the mmap object *could* handle
   a larger map size, there is no point because all the useful operations
   (len(), slicing(), sequence indexing) are limited by a C int.

746
   Returns -1 on error, with an appropriate Python exception raised. On
747 748
   success, the map size is returned. */
static int
Fredrik Lundh's avatar
Fredrik Lundh committed
749
_GetMapSize(PyObject *o)
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
{
	if (PyInt_Check(o)) {
		long i = PyInt_AsLong(o);
		if (PyErr_Occurred())
			return -1;
		if (i < 0)
			goto onnegoverflow;
		if (i > INT_MAX)
			goto onposoverflow;
		return (int)i;
	}
	else if (PyLong_Check(o)) {
		long i = PyLong_AsLong(o);
		if (PyErr_Occurred()) {
			/* yes negative overflow is mistaken for positive overflow
			   but not worth the trouble to check sign of 'i' */
			if (PyErr_ExceptionMatches(PyExc_OverflowError))
				goto onposoverflow;
			else
				return -1;
		}
		if (i < 0)
			goto onnegoverflow;
		if (i > INT_MAX)
			goto onposoverflow;
		return (int)i;
	}
	else {
		PyErr_SetString(PyExc_TypeError,
			"map size must be an integral value");
		return -1;
	}

onnegoverflow:
	PyErr_SetString(PyExc_OverflowError,
		"memory mapped size must be positive");
	return -1;

onposoverflow:
	PyErr_SetString(PyExc_OverflowError,
		"memory mapped size is too large (limited by C int)");
	return -1;
}

794 795
#ifdef UNIX 
static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
796
new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
797
{
Fredrik Lundh's avatar
Fredrik Lundh committed
798
	mmap_object *m_obj;
799 800
	PyObject *map_size_obj = NULL;
	int map_size;
801 802 803 804
	int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
	char *keywords[] = {"file", "size", "flags", "prot", NULL};

	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
805 806
					 "iO|ii", keywords, 
					 &fd, &map_size_obj, &flags, &prot)
807 808
		)
		return NULL;
809 810 811 812
	map_size = _GetMapSize(map_size_obj);
	if (map_size < 0)
		return NULL;
	
813
	m_obj = PyObject_New (mmap_object, &mmap_object_type);
814 815 816
	if (m_obj == NULL) {return NULL;}
	m_obj->size = (size_t) map_size;
	m_obj->pos = (size_t) 0;
817
	m_obj->fd = fd;
818 819 820 821 822 823 824 825 826 827
	m_obj->data = mmap(NULL, map_size, 
			   prot, flags,
			   fd, 0);
	if (m_obj->data == (void *)-1)
	{
		Py_DECREF(m_obj);
		PyErr_SetFromErrno(mmap_module_error);
		return NULL;
	}
	return (PyObject *)m_obj;
828 829 830 831 832
}
#endif /* UNIX */

#ifdef MS_WIN32
static PyObject *
Fredrik Lundh's avatar
Fredrik Lundh committed
833
new_mmap_object(PyObject *self, PyObject *args)
834
{
Fredrik Lundh's avatar
Fredrik Lundh committed
835
	mmap_object *m_obj;
836 837
	PyObject *map_size_obj = NULL;
	int map_size;
Fredrik Lundh's avatar
Fredrik Lundh committed
838
	char *tagname = "";
839 840 841

	DWORD dwErr = 0;
	int fileno;
Mark Hammond's avatar
Mark Hammond committed
842
	HANDLE fh = 0;
843 844

	if (!PyArg_ParseTuple(args,
845
			  "iO|z",
846
			  &fileno,
847
			  &map_size_obj,
848 849 850
			  &tagname)
		)
		return NULL;
851
  
852 853 854 855
	map_size = _GetMapSize(map_size_obj);
	if (map_size < 0)
		return NULL;
	
856 857
	/* if an actual filename has been specified */
	if (fileno != 0) {
Mark Hammond's avatar
Mark Hammond committed
858 859
		fh = (HANDLE)_get_osfhandle(fileno);
		if (fh==(HANDLE)-1) {
860 861
		    PyErr_SetFromErrno(mmap_module_error);
		    return NULL;
862
		}
863 864
		/* Win9x appears to need us seeked to zero */
		fseek(&_iob[fileno], 0, SEEK_SET);
865 866
	}

867
	m_obj = PyObject_New (mmap_object, &mmap_object_type);
868 869 870 871 872
	if (m_obj==NULL)
		return NULL;
	/* Set every field to an invalid marker, so we can safely
	   destruct the object in the face of failure */
	m_obj->data = NULL;
Mark Hammond's avatar
Mark Hammond committed
873
	m_obj->file_handle = INVALID_HANDLE_VALUE;
874 875 876
	m_obj->map_handle = INVALID_HANDLE_VALUE;
	m_obj->tagname = NULL;

877
	if (fh) {
878 879 880 881
		/* It is necessary to duplicate the handle, so the
		   Python code can close it on us */
		if (!DuplicateHandle(
			    GetCurrentProcess(), /* source process handle */
Mark Hammond's avatar
Mark Hammond committed
882
			    fh, /* handle to be duplicated */
883 884 885 886 887 888 889 890 891 892
			    GetCurrentProcess(), /* target proc handle */
			    (LPHANDLE)&m_obj->file_handle, /* result */
			    0, /* access - ignored due to options value */
			    FALSE, /* inherited by child processes? */
			    DUPLICATE_SAME_ACCESS)) { /* options */
			dwErr = GetLastError();
			Py_DECREF(m_obj);
			PyErr_SetFromWindowsErr(dwErr);
			return NULL;
		}
893
		if (!map_size) {
Mark Hammond's avatar
Mark Hammond committed
894
			m_obj->size = GetFileSize (fh, NULL);
895 896
		} else {
			m_obj->size = map_size;
897
		}
898 899 900 901 902 903 904 905
	}
	else {
		m_obj->size = map_size;
	}

	/* set the initial position */
	m_obj->pos = (size_t) 0;

906
	/* set the tag name */
907
	if (tagname != NULL && *tagname != '\0') {
908 909 910 911 912 913 914 915 916 917 918
		m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
		if (m_obj->tagname == NULL) {
			PyErr_NoMemory();
			Py_DECREF(m_obj);
			return NULL;
		}
		strcpy(m_obj->tagname, tagname);
	}
	else
		m_obj->tagname = NULL;

Mark Hammond's avatar
Mark Hammond committed
919
	m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
920 921 922 923
					       NULL,
					       PAGE_READWRITE,
					       0,
					       m_obj->size,
924
					       m_obj->tagname);
925 926 927 928 929 930 931 932
	if (m_obj->map_handle != NULL) {
		m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
						      FILE_MAP_WRITE,
						      0,
						      0,
						      0);
		if (m_obj->data != NULL) {
			return ((PyObject *) m_obj);
933
		} else {
934
		    dwErr = GetLastError();
935
		}
936 937 938
	} else {
		dwErr = GetLastError();
	}
939
	Py_DECREF(m_obj);
940 941
	PyErr_SetFromWindowsErr(dwErr);
	return (NULL);
942 943 944 945 946
}
#endif /* MS_WIN32 */

/* List of functions exported by this module */
static struct PyMethodDef mmap_functions[] = {
947 948 949
	{"mmap",	(PyCFunction) new_mmap_object, 
	 METH_VARARGS|METH_KEYWORDS},
	{NULL,		NULL}	     /* Sentinel */
950 951
};

952
DL_EXPORT(void)
953 954
initmmap(void)
{
955
	PyObject *dict, *module;
956 957 958 959

	/* Patch the object type */
	mmap_object_type.ob_type = &PyType_Type;

960 961 962 963 964
	module = Py_InitModule ("mmap", mmap_functions);
	dict = PyModule_GetDict (module);
	mmap_module_error = PyExc_EnvironmentError;
	Py_INCREF(mmap_module_error);
	PyDict_SetItemString (dict, "error", mmap_module_error);
965
#ifdef PROT_EXEC
966
	PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) );
967 968
#endif
#ifdef PROT_READ
969
	PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) );
970 971
#endif
#ifdef PROT_WRITE
972
	PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) );
973 974 975
#endif

#ifdef MAP_SHARED
976
	PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) );
977 978
#endif
#ifdef MAP_PRIVATE
979 980
	PyDict_SetItemString (dict, "MAP_PRIVATE",
			      PyInt_FromLong(MAP_PRIVATE) );
981 982
#endif
#ifdef MAP_DENYWRITE
983 984
	PyDict_SetItemString (dict, "MAP_DENYWRITE",
			      PyInt_FromLong(MAP_DENYWRITE) );
985 986
#endif
#ifdef MAP_EXECUTABLE
987 988
	PyDict_SetItemString (dict, "MAP_EXECUTABLE",
			      PyInt_FromLong(MAP_EXECUTABLE) );
989 990
#endif
#ifdef MAP_ANON
991 992 993
	PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANON) );
	PyDict_SetItemString (dict, "MAP_ANONYMOUS",
			      PyInt_FromLong(MAP_ANON) );
994 995
#endif

996
	PyDict_SetItemString (dict, "PAGESIZE",
997
			      PyInt_FromLong( (long)my_getpagesize() ) );
998
}
999