Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(9)

Delta Between Two Patch Sets: Objects/memoryobject.c

Issue 15573: Support unknown formats in memoryview comparisons
Left Patch Set: Created 7 years, 7 months ago
Right Patch Set: Created 7 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « Lib/test/test_buffer.py ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* Memoryview object implementation */ 1 /* Memoryview object implementation */
2 2
3 #include "Python.h" 3 #include "Python.h"
4 #include <stddef.h> 4 #include <stddef.h>
5 5
6 6
7 /****************************************************************************/ 7 /****************************************************************************/
8 /* ManagedBuffer Object */ 8 /* ManagedBuffer Object */
9 /****************************************************************************/ 9 /****************************************************************************/
10 10
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 Py_LOCAL_INLINE(int) 248 Py_LOCAL_INLINE(int)
249 last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src) 249 last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
250 { 250 {
251 assert(dest->ndim > 0 && src->ndim > 0); 251 assert(dest->ndim > 0 && src->ndim > 0);
252 return (!HAVE_SUBOFFSETS_IN_LAST_DIM(dest) && 252 return (!HAVE_SUBOFFSETS_IN_LAST_DIM(dest) &&
253 !HAVE_SUBOFFSETS_IN_LAST_DIM(src) && 253 !HAVE_SUBOFFSETS_IN_LAST_DIM(src) &&
254 dest->strides[dest->ndim-1] == dest->itemsize && 254 dest->strides[dest->ndim-1] == dest->itemsize &&
255 src->strides[src->ndim-1] == src->itemsize); 255 src->strides[src->ndim-1] == src->itemsize);
256 } 256 }
257 257
258 /* This is not a general function for determining format equivalence.
259 It is used in copy_single() and copy_buffer() to weed out non-matching
260 formats. Skipping the '@' character is specifically used in slice
261 assignments, where the lvalue is already known to have a single character
262 format. This is a performance hack that could be rewritten (if properly
263 benchmarked). */
258 Py_LOCAL_INLINE(int) 264 Py_LOCAL_INLINE(int)
259 cmp_format(const Py_buffer *dest, const Py_buffer *src) 265 equiv_format(const Py_buffer *dest, const Py_buffer *src)
Nick Coghlan 2012/08/14 15:12:38 This doesn't appear to be used anywhere at the mom
skrah 2012/08/14 16:31:58 OK, perhaps equiv_format() is appropriate since th
260 { 266 {
261 const char *dfmt, *sfmt; 267 const char *dfmt, *sfmt;
262 268
263 assert(dest->format && src->format); 269 assert(dest->format && src->format);
264 dfmt = dest->format[0] == '@' ? dest->format+1 : dest->format; 270 dfmt = dest->format[0] == '@' ? dest->format+1 : dest->format;
265 sfmt = src->format[0] == '@' ? src->format+1 : src->format; 271 sfmt = src->format[0] == '@' ? src->format+1 : src->format;
266 272
267 if (strcmp(dfmt, sfmt) != 0 || 273 if (strcmp(dfmt, sfmt) != 0 ||
268 dest->itemsize != src->itemsize) { 274 dest->itemsize != src->itemsize) {
269 return -1; 275 return 0;
270 } 276 }
271 277
272 return 0; 278 return 1;
273 } 279 }
274 280
281 /* Two shapes are equivalent if they are either equal or identical up
282 to a zero element at the same position. For example, in NumPy arrays
283 the shapes [1, 0, 5] and [1, 0, 7] are equivalent. */
275 Py_LOCAL_INLINE(int) 284 Py_LOCAL_INLINE(int)
276 cmp_shape(const Py_buffer *dest, const Py_buffer *src) 285 equiv_shape(const Py_buffer *dest, const Py_buffer *src)
Nick Coghlan 2012/08/14 15:12:38 This API is rather confusing, since it is a simple
skrah 2012/08/14 16:31:58 I agree that it doesn't follow the standard patter
Nick Coghlan 2012/08/15 06:13:09 +1, that would be much easier to interpret. It's m
skrah 2012/08/15 16:45:02 OK, done.
277 { 286 {
278 int i; 287 int i;
279 288
280 if (dest->ndim != src->ndim) 289 if (dest->ndim != src->ndim)
281 return -1; 290 return 0;
282 291
283 for (i = 0; i < dest->ndim; i++) { 292 for (i = 0; i < dest->ndim; i++) {
284 if (dest->shape[i] != src->shape[i]) 293 if (dest->shape[i] != src->shape[i])
285 return -1; 294 return 0;
286 if (dest->shape[i] == 0) 295 if (dest->shape[i] == 0)
287 break; 296 break;
288 } 297 }
289 298
290 return 0; 299 return 1;
291 } 300 }
292 301
293 /* Check that the logical structure of the destination and source buffers 302 /* Check that the logical structure of the destination and source buffers
294 is identical. */ 303 is identical. */
295 static int 304 static int
296 cmp_structure(const Py_buffer *dest, const Py_buffer *src) 305 equiv_structure(const Py_buffer *dest, const Py_buffer *src)
Nick Coghlan 2012/08/14 15:12:38 This appears to be unused now. Why not just delete
skrah 2012/08/14 16:31:58 It's still used in copy_single() and copy_buffer()
Nick Coghlan 2012/08/15 06:13:09 Yeah, this was a silly comment born of searching f
297 { 306 {
298 if (cmp_format(dest, src) < 0 || 307 if (!equiv_format(dest, src) ||
299 cmp_shape(dest, src) < 0) { 308 !equiv_shape(dest, src)) {
300 PyErr_SetString(PyExc_ValueError, 309 PyErr_SetString(PyExc_ValueError,
301 "ndarray assignment: lvalue and rvalue have different structures"); 310 "ndarray assignment: lvalue and rvalue have different structures");
302 return -1; 311 return 0;
303 } 312 }
304 313
305 return 0; 314 return 1;
306 } 315 }
307 316
308 /* Base case for recursive multi-dimensional copying. Contiguous arrays are 317 /* Base case for recursive multi-dimensional copying. Contiguous arrays are
309 copied with very little overhead. Assumptions: ndim == 1, mem == NULL or 318 copied with very little overhead. Assumptions: ndim == 1, mem == NULL or
310 sizeof(mem) == shape[0] * itemsize. */ 319 sizeof(mem) == shape[0] * itemsize. */
311 static void 320 static void
312 copy_base(const Py_ssize_t *shape, Py_ssize_t itemsize, 321 copy_base(const Py_ssize_t *shape, Py_ssize_t itemsize,
313 char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets, 322 char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets,
314 char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets, 323 char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets,
315 char *mem) 324 char *mem)
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 } 377 }
369 378
370 /* Faster copying of one-dimensional arrays. */ 379 /* Faster copying of one-dimensional arrays. */
371 static int 380 static int
372 copy_single(Py_buffer *dest, Py_buffer *src) 381 copy_single(Py_buffer *dest, Py_buffer *src)
373 { 382 {
374 char *mem = NULL; 383 char *mem = NULL;
375 384
376 assert(dest->ndim == 1); 385 assert(dest->ndim == 1);
377 386
378 if (cmp_structure(dest, src) < 0) 387 if (!equiv_structure(dest, src))
379 return -1; 388 return -1;
380 389
381 if (!last_dim_is_contiguous(dest, src)) { 390 if (!last_dim_is_contiguous(dest, src)) {
382 mem = PyMem_Malloc(dest->shape[0] * dest->itemsize); 391 mem = PyMem_Malloc(dest->shape[0] * dest->itemsize);
383 if (mem == NULL) { 392 if (mem == NULL) {
384 PyErr_NoMemory(); 393 PyErr_NoMemory();
385 return -1; 394 return -1;
386 } 395 }
387 } 396 }
388 397
(...skipping 11 matching lines...) Expand all
400 /* Recursively copy src to dest. Both buffers must have the same basic 409 /* Recursively copy src to dest. Both buffers must have the same basic
401 structure. Copying is atomic, the function never fails with a partial 410 structure. Copying is atomic, the function never fails with a partial
402 copy. */ 411 copy. */
403 static int 412 static int
404 copy_buffer(Py_buffer *dest, Py_buffer *src) 413 copy_buffer(Py_buffer *dest, Py_buffer *src)
405 { 414 {
406 char *mem = NULL; 415 char *mem = NULL;
407 416
408 assert(dest->ndim > 0); 417 assert(dest->ndim > 0);
409 418
410 if (cmp_structure(dest, src) < 0) 419 if (!equiv_structure(dest, src))
411 return -1; 420 return -1;
412 421
413 if (!last_dim_is_contiguous(dest, src)) { 422 if (!last_dim_is_contiguous(dest, src)) {
414 mem = PyMem_Malloc(dest->shape[dest->ndim-1] * dest->itemsize); 423 mem = PyMem_Malloc(dest->shape[dest->ndim-1] * dest->itemsize);
415 if (mem == NULL) { 424 if (mem == NULL) {
416 PyErr_NoMemory(); 425 PyErr_NoMemory();
417 return -1; 426 return -1;
418 } 427 }
419 } 428 }
420 429
(...skipping 1519 matching lines...) Expand 10 before | Expand all | Expand 10 after
1940 Py_XDECREF(format); 1949 Py_XDECREF(format);
1941 Py_XDECREF(structobj); 1950 Py_XDECREF(structobj);
1942 return x; 1951 return x;
1943 1952
1944 error: 1953 error:
1945 unpacker_free(x); 1954 unpacker_free(x);
1946 x = NULL; 1955 x = NULL;
1947 goto out; 1956 goto out;
1948 } 1957 }
1949 1958
1959 /* unpack a single item */
1950 static PyObject * 1960 static PyObject *
1951 struct_unpack_single(const char *ptr, struct unpacker *x) 1961 struct_unpack_single(const char *ptr, struct unpacker *x)
1952 { 1962 {
1953 PyObject *v; 1963 PyObject *v;
1954 1964
1955 memcpy(x->item, ptr, x->itemsize); 1965 memcpy(x->item, ptr, x->itemsize);
1956 v = PyObject_CallFunctionObjArgs(x->unpack_from, x->mview, NULL); 1966 v = PyObject_CallFunctionObjArgs(x->unpack_from, x->mview, NULL);
1957 if (v == NULL) 1967 if (v == NULL)
1958 return NULL; 1968 return NULL;
1959 1969
(...skipping 652 matching lines...) Expand 10 before | Expand all | Expand 10 after
2612 ww = VIEW_ADDR(w); 2622 ww = VIEW_ADDR(w);
2613 } 2623 }
2614 else { 2624 else {
2615 if (PyObject_GetBuffer(w, &wbuf, PyBUF_FULL_RO) < 0) { 2625 if (PyObject_GetBuffer(w, &wbuf, PyBUF_FULL_RO) < 0) {
2616 PyErr_Clear(); 2626 PyErr_Clear();
2617 goto result; /* Py_NotImplemented */ 2627 goto result; /* Py_NotImplemented */
2618 } 2628 }
2619 ww = &wbuf; 2629 ww = &wbuf;
2620 } 2630 }
2621 2631
2622 if (cmp_shape(vv, ww) < 0) { 2632 if (!equiv_shape(vv, ww)) {
2623 PyErr_Clear(); 2633 PyErr_Clear();
2624 equal = 0; 2634 equal = 0;
2625 goto result; 2635 goto result;
2626 } 2636 }
2627 2637
Nick Coghlan 2012/08/14 15:12:38 This is where I would have expected to see a fast
skrah 2012/08/14 16:31:58 The NaN test case wouldn't pass memcpy(). Also, Ma
Nick Coghlan 2012/08/15 06:13:09 A comment here explaining why a memcpy() fast path
skrah 2012/08/15 16:45:02 Also done.
2638 /* Use fast unpacking for identical primitive C type formats. */
2628 if (get_native_fmtchar(&vfmt, vv->format) < 0) 2639 if (get_native_fmtchar(&vfmt, vv->format) < 0)
2629 vfmt = '_'; 2640 vfmt = '_';
2630 if (get_native_fmtchar(&wfmt, ww->format) < 0) 2641 if (get_native_fmtchar(&wfmt, ww->format) < 0)
2631 wfmt = '_'; 2642 wfmt = '_';
2632 if (vfmt == '_' || wfmt == '_' || vfmt != wfmt) { 2643 if (vfmt == '_' || wfmt == '_' || vfmt != wfmt) {
2644 /* Use struct module unpacking. NOTE: Even for equal format strings,
2645 memcmp() cannot be used for item comparison since it would give
2646 incorrect results in the case of NaNs or uninitialized padding
2647 bytes. */
2633 vfmt = '_'; 2648 vfmt = '_';
2634 unpack_v = struct_get_unpacker(vv->format, vv->itemsize); 2649 unpack_v = struct_get_unpacker(vv->format, vv->itemsize);
2635 if (unpack_v == NULL) { 2650 if (unpack_v == NULL) {
2636 equal = fix_struct_error_int(); 2651 equal = fix_struct_error_int();
2637 goto result; 2652 goto result;
2638 } 2653 }
2639 unpack_w = struct_get_unpacker(ww->format, ww->itemsize); 2654 unpack_w = struct_get_unpacker(ww->format, ww->itemsize);
2640 if (unpack_w == NULL) { 2655 if (unpack_w == NULL) {
2641 equal = fix_struct_error_int(); 2656 equal = fix_struct_error_int();
2642 goto result; 2657 goto result;
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
2907 memory_getsetlist, /* tp_getset */ 2922 memory_getsetlist, /* tp_getset */
2908 0, /* tp_base */ 2923 0, /* tp_base */
2909 0, /* tp_dict */ 2924 0, /* tp_dict */
2910 0, /* tp_descr_get */ 2925 0, /* tp_descr_get */
2911 0, /* tp_descr_set */ 2926 0, /* tp_descr_set */
2912 0, /* tp_dictoffset */ 2927 0, /* tp_dictoffset */
2913 0, /* tp_init */ 2928 0, /* tp_init */
2914 0, /* tp_alloc */ 2929 0, /* tp_alloc */
2915 memory_new, /* tp_new */ 2930 memory_new, /* tp_new */
2916 }; 2931 };
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+