Skip to content

Commit f15f573

Browse files
authored
Merge pull request #5224 from radarhere/mapper
2 parents a95fee0 + 11cb3fb commit f15f573

File tree

4 files changed

+15
-287
lines changed

4 files changed

+15
-287
lines changed

Tests/test_map.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
from PIL import Image
66

7-
from .helper import is_win32
8-
9-
pytestmark = pytest.mark.skipif(is_win32(), reason="Win32 does not call map_buffer")
10-
117

128
def test_overflow():
139
# There is the potential to overflow comparisons in map.c
@@ -27,6 +23,13 @@ def test_overflow():
2723
Image.MAX_IMAGE_PIXELS = max_pixels
2824

2925

26+
def test_tobytes():
27+
# Previously raised an access violation on Windows
28+
with Image.open("Tests/images/l2rgb_read.bmp") as im:
29+
with pytest.raises((ValueError, MemoryError, OSError)):
30+
im.tobytes()
31+
32+
3033
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system")
3134
def test_ysize():
3235
numpy = pytest.importorskip("numpy", reason="NumPy not installed")

src/PIL/ImageFile.py

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -192,24 +192,14 @@ def load(self):
192192
and args[0] in Image._MAPMODES
193193
):
194194
try:
195-
if hasattr(Image.core, "map"):
196-
# use built-in mapper WIN32 only
197-
self.map = Image.core.map(self.filename)
198-
self.map.seek(offset)
199-
self.im = self.map.readimage(
200-
self.mode, self.size, args[1], args[2]
201-
)
202-
else:
203-
# use mmap, if possible
204-
import mmap
205-
206-
with open(self.filename) as fp:
207-
self.map = mmap.mmap(
208-
fp.fileno(), 0, access=mmap.ACCESS_READ
209-
)
210-
self.im = Image.core.map_buffer(
211-
self.map, self.size, decoder_name, offset, args
212-
)
195+
# use mmap, if possible
196+
import mmap
197+
198+
with open(self.filename) as fp:
199+
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
200+
self.im = Image.core.map_buffer(
201+
self.map, self.size, decoder_name, offset, args
202+
)
213203
readonly = 1
214204
# After trashing self.im,
215205
# we might need to reload the palette data.

src/_imaging.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3973,8 +3973,6 @@ PyPath_Create(ImagingObject *self, PyObject *args);
39733973
extern PyObject *
39743974
PyOutline_Create(ImagingObject *self, PyObject *args);
39753975

3976-
extern PyObject *
3977-
PyImaging_Mapper(PyObject *self, PyObject *args);
39783976
extern PyObject *
39793977
PyImaging_MapBuffer(PyObject *self, PyObject *args);
39803978

@@ -4030,9 +4028,6 @@ static PyMethodDef functions[] = {
40304028

40314029
/* Memory mapping */
40324030
#ifdef WITH_MAPPING
4033-
#ifdef _WIN32
4034-
{"map", (PyCFunction)PyImaging_Mapper, 1},
4035-
#endif
40364031
{"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1},
40374032
#endif
40384033

src/map.c

Lines changed: 0 additions & 260 deletions
Original file line numberDiff line numberDiff line change
@@ -28,269 +28,9 @@ PyImaging_CheckBuffer(PyObject *buffer);
2828
extern int
2929
PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view);
3030

31-
/* -------------------------------------------------------------------- */
32-
/* Standard mapper */
33-
34-
typedef struct {
35-
PyObject_HEAD char *base;
36-
int size;
37-
int offset;
38-
#ifdef _WIN32
39-
HANDLE hFile;
40-
HANDLE hMap;
41-
#endif
42-
} ImagingMapperObject;
43-
44-
static PyTypeObject ImagingMapperType;
45-
46-
ImagingMapperObject *
47-
PyImaging_MapperNew(const char *filename, int readonly) {
48-
ImagingMapperObject *mapper;
49-
50-
if (PyType_Ready(&ImagingMapperType) < 0) {
51-
return NULL;
52-
}
53-
54-
mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType);
55-
if (mapper == NULL) {
56-
return NULL;
57-
}
58-
59-
mapper->base = NULL;
60-
mapper->size = mapper->offset = 0;
61-
62-
#ifdef _WIN32
63-
mapper->hFile = (HANDLE)-1;
64-
mapper->hMap = (HANDLE)-1;
65-
66-
/* FIXME: currently supports readonly mappings only */
67-
mapper->hFile = CreateFile(
68-
filename,
69-
GENERIC_READ,
70-
FILE_SHARE_READ,
71-
NULL,
72-
OPEN_EXISTING,
73-
FILE_ATTRIBUTE_NORMAL,
74-
NULL);
75-
if (mapper->hFile == (HANDLE)-1) {
76-
PyErr_SetString(PyExc_OSError, "cannot open file");
77-
Py_DECREF(mapper);
78-
return NULL;
79-
}
80-
81-
mapper->hMap = CreateFileMapping(mapper->hFile, NULL, PAGE_READONLY, 0, 0, NULL);
82-
if (mapper->hMap == (HANDLE)-1) {
83-
CloseHandle(mapper->hFile);
84-
PyErr_SetString(PyExc_OSError, "cannot map file");
85-
Py_DECREF(mapper);
86-
return NULL;
87-
}
88-
89-
mapper->base = (char *)MapViewOfFile(mapper->hMap, FILE_MAP_READ, 0, 0, 0);
90-
91-
mapper->size = GetFileSize(mapper->hFile, 0);
92-
#endif
93-
94-
return mapper;
95-
}
96-
97-
static void
98-
mapping_dealloc(ImagingMapperObject *mapper) {
99-
#ifdef _WIN32
100-
if (mapper->base != 0) {
101-
UnmapViewOfFile(mapper->base);
102-
}
103-
if (mapper->hMap != (HANDLE)-1) {
104-
CloseHandle(mapper->hMap);
105-
}
106-
if (mapper->hFile != (HANDLE)-1) {
107-
CloseHandle(mapper->hFile);
108-
}
109-
mapper->base = 0;
110-
mapper->hMap = mapper->hFile = (HANDLE)-1;
111-
#endif
112-
PyObject_Del(mapper);
113-
}
114-
115-
/* -------------------------------------------------------------------- */
116-
/* standard file operations */
117-
118-
static PyObject *
119-
mapping_read(ImagingMapperObject *mapper, PyObject *args) {
120-
PyObject *buf;
121-
122-
int size = -1;
123-
if (!PyArg_ParseTuple(args, "|i", &size)) {
124-
return NULL;
125-
}
126-
127-
/* check size */
128-
if (size < 0 || mapper->offset + size > mapper->size) {
129-
size = mapper->size - mapper->offset;
130-
}
131-
if (size < 0) {
132-
size = 0;
133-
}
134-
135-
buf = PyBytes_FromStringAndSize(NULL, size);
136-
if (!buf) {
137-
return NULL;
138-
}
139-
140-
if (size > 0) {
141-
memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size);
142-
mapper->offset += size;
143-
}
144-
145-
return buf;
146-
}
147-
148-
static PyObject *
149-
mapping_seek(ImagingMapperObject *mapper, PyObject *args) {
150-
int offset;
151-
int whence = 0;
152-
if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) {
153-
return NULL;
154-
}
155-
156-
switch (whence) {
157-
case 0: /* SEEK_SET */
158-
mapper->offset = offset;
159-
break;
160-
case 1: /* SEEK_CUR */
161-
mapper->offset += offset;
162-
break;
163-
case 2: /* SEEK_END */
164-
mapper->offset = mapper->size + offset;
165-
break;
166-
default:
167-
/* FIXME: raise ValueError? */
168-
break;
169-
}
170-
171-
Py_INCREF(Py_None);
172-
return Py_None;
173-
}
174-
175-
/* -------------------------------------------------------------------- */
176-
/* map entire image */
177-
17831
extern PyObject *
17932
PyImagingNew(Imaging im);
18033

181-
static void
182-
ImagingDestroyMap(Imaging im) {
183-
return; /* nothing to do! */
184-
}
185-
186-
static PyObject *
187-
mapping_readimage(ImagingMapperObject *mapper, PyObject *args) {
188-
int y, size;
189-
Imaging im;
190-
191-
char *mode;
192-
int xsize;
193-
int ysize;
194-
int stride;
195-
int orientation;
196-
if (!PyArg_ParseTuple(
197-
args, "s(ii)ii", &mode, &xsize, &ysize, &stride, &orientation)) {
198-
return NULL;
199-
}
200-
201-
if (stride <= 0) {
202-
/* FIXME: maybe we should call ImagingNewPrologue instead */
203-
if (!strcmp(mode, "L") || !strcmp(mode, "P")) {
204-
stride = xsize;
205-
} else if (!strcmp(mode, "I;16") || !strcmp(mode, "I;16B")) {
206-
stride = xsize * 2;
207-
} else {
208-
stride = xsize * 4;
209-
}
210-
}
211-
212-
size = ysize * stride;
213-
214-
if (mapper->offset + size > mapper->size) {
215-
PyErr_SetString(PyExc_OSError, "image file truncated");
216-
return NULL;
217-
}
218-
219-
im = ImagingNewPrologue(mode, xsize, ysize);
220-
if (!im) {
221-
return NULL;
222-
}
223-
224-
/* setup file pointers */
225-
if (orientation > 0) {
226-
for (y = 0; y < ysize; y++) {
227-
im->image[y] = mapper->base + mapper->offset + y * stride;
228-
}
229-
} else {
230-
for (y = 0; y < ysize; y++) {
231-
im->image[ysize - y - 1] = mapper->base + mapper->offset + y * stride;
232-
}
233-
}
234-
235-
im->destroy = ImagingDestroyMap;
236-
237-
mapper->offset += size;
238-
239-
return PyImagingNew(im);
240-
}
241-
242-
static struct PyMethodDef methods[] = {
243-
/* standard file interface */
244-
{"read", (PyCFunction)mapping_read, 1},
245-
{"seek", (PyCFunction)mapping_seek, 1},
246-
/* extensions */
247-
{"readimage", (PyCFunction)mapping_readimage, 1},
248-
{NULL, NULL} /* sentinel */
249-
};
250-
251-
static PyTypeObject ImagingMapperType = {
252-
PyVarObject_HEAD_INIT(NULL, 0) "ImagingMapper", /*tp_name*/
253-
sizeof(ImagingMapperObject), /*tp_size*/
254-
0, /*tp_itemsize*/
255-
/* methods */
256-
(destructor)mapping_dealloc, /*tp_dealloc*/
257-
0, /*tp_print*/
258-
0, /*tp_getattr*/
259-
0, /*tp_setattr*/
260-
0, /*tp_compare*/
261-
0, /*tp_repr*/
262-
0, /*tp_as_number */
263-
0, /*tp_as_sequence */
264-
0, /*tp_as_mapping */
265-
0, /*tp_hash*/
266-
0, /*tp_call*/
267-
0, /*tp_str*/
268-
0, /*tp_getattro*/
269-
0, /*tp_setattro*/
270-
0, /*tp_as_buffer*/
271-
Py_TPFLAGS_DEFAULT, /*tp_flags*/
272-
0, /*tp_doc*/
273-
0, /*tp_traverse*/
274-
0, /*tp_clear*/
275-
0, /*tp_richcompare*/
276-
0, /*tp_weaklistoffset*/
277-
0, /*tp_iter*/
278-
0, /*tp_iternext*/
279-
methods, /*tp_methods*/
280-
0, /*tp_members*/
281-
0, /*tp_getset*/
282-
};
283-
284-
PyObject *
285-
PyImaging_Mapper(PyObject *self, PyObject *args) {
286-
char *filename;
287-
if (!PyArg_ParseTuple(args, "s", &filename)) {
288-
return NULL;
289-
}
290-
291-
return (PyObject *)PyImaging_MapperNew(filename, 1);
292-
}
293-
29434
/* -------------------------------------------------------------------- */
29535
/* Buffer mapper */
29636

0 commit comments

Comments
 (0)