diff options
author | njsmith <njs@pobox.com> | 2012-07-11 13:59:43 -0700 |
---|---|---|
committer | njsmith <njs@pobox.com> | 2012-07-11 13:59:43 -0700 |
commit | 0920bede09b4a467c9c4e977607c38fd81371087 (patch) | |
tree | e8e046c4b829ba3e689220c0abcdbc55c66f789f | |
parent | f4c47a5e366d39f225a19db78010d9785dd801de (diff) | |
parent | d2c8843f0ec8a3b2f81b062b346a94ed5331d4a3 (diff) | |
download | numpy-0920bede09b4a467c9c4e977607c38fd81371087.tar.gz |
Merge pull request #324 from walshb/fix_memcpy_overlap
Use memmove instead of memcpy for strided copies, since src and dst can overlap.
-rw-r--r-- | numpy/core/src/multiarray/lowlevel_strided_loops.c.src | 14 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray_assignment.py | 78 |
2 files changed, 85 insertions, 7 deletions
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src index c57e1ccc1..78818b31d 100644 --- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src +++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src @@ -151,7 +151,7 @@ static void #else /* unaligned copy and swap */ - memcpy(dst, src, @elsize@); + memmove(dst, src, @elsize@); # if @is_swap@ == 1 @swap@@elsize@(dst); # elif @is_swap@ == 2 @@ -239,7 +239,7 @@ _strided_to_strided(char *dst, npy_intp dst_stride, NpyAuxData *NPY_UNUSED(data)) { while (N > 0) { - memcpy(dst, src, src_itemsize); + memmove(dst, src, src_itemsize); dst += dst_stride; src += src_stride; --N; @@ -255,7 +255,7 @@ _swap_strided_to_strided(char *dst, npy_intp dst_stride, char *a, *b, c; while (N > 0) { - memcpy(dst, src, src_itemsize); + memmove(dst, src, src_itemsize); /* general in-place swap */ a = dst; b = dst + src_itemsize - 1; @@ -281,7 +281,7 @@ _swap_pair_strided_to_strided(char *dst, npy_intp dst_stride, npy_intp itemsize_half = src_itemsize / 2; while (N > 0) { - memcpy(dst, src, src_itemsize); + memmove(dst, src, src_itemsize); /* general in-place swap */ a = dst; b = dst + itemsize_half - 1; @@ -312,7 +312,7 @@ _contig_to_contig(char *dst, npy_intp NPY_UNUSED(dst_stride), npy_intp N, npy_intp src_itemsize, NpyAuxData *NPY_UNUSED(data)) { - memcpy(dst, src, src_itemsize*N); + memmove(dst, src, src_itemsize*N); } @@ -802,7 +802,7 @@ static void src_value = *((_TYPE1 *)src); # endif #else - memcpy(&src_value, src, sizeof(src_value)); + memmove(&src_value, src, sizeof(src_value)); #endif /* Do the cast */ @@ -838,7 +838,7 @@ static void *((_TYPE2 *)dst) = dst_value; # endif #else - memcpy(dst, &dst_value, sizeof(dst_value)); + memmove(dst, &dst_value, sizeof(dst_value)); #endif #if @contig@ diff --git a/numpy/core/tests/test_multiarray_assignment.py b/numpy/core/tests/test_multiarray_assignment.py new file mode 100644 index 000000000..29ddcf906 --- /dev/null +++ b/numpy/core/tests/test_multiarray_assignment.py @@ -0,0 +1,78 @@ +import numpy as np +from numpy.testing import TestCase + +ndims = 2 +size = 10 +shape = tuple([size] * ndims) + + +def _indices_for_nelems(nelems): + """Returns slices of length nelems, from start onwards, in direction sign.""" + + if nelems == 0: + return [size // 2] # int index + + res = [] + for step in (1, 2): + for sign in (-1, 1): + start = size // 2 - nelems * step * sign // 2 + stop = start + nelems * step * sign + res.append(slice(start, stop, step * sign)) + + return res + + +def _indices_for_axis(): + """Returns (src, dst) pairs of indices.""" + + res = [] + for nelems in (0, 2, 3): + ind = _indices_for_nelems(nelems) + + # no itertools.product available in Py2.4 + res.extend([(a, b) for a in ind for b in ind]) # all assignments of size "nelems" + + return res + + +def _indices(ndims): + """Returns ((axis0_src, axis0_dst), (axis1_src, axis1_dst), ... ) index pairs.""" + + ind = _indices_for_axis() + + # no itertools.product available in Py2.4 + + res = [[]] + for i in xrange(ndims): + newres = [] + for elem in ind: + for others in res: + newres.append([elem] + others) + res = newres + + return res + + +def _check_assignment(srcidx, dstidx): + """Check assignment arr[dstidx] = arr[srcidx] works.""" + + arr = np.arange(np.product(shape)).reshape(shape) + + cpy = arr.copy() + + cpy[dstidx] = arr[srcidx] + arr[dstidx] = arr[srcidx] + + assert np.all(arr == cpy), 'assigning arr[%s] = arr[%s]' % (dstidx, srcidx) + + +def test_overlapping_assignments(): + """Test automatically generated assignments which overlap in memory.""" + + inds = _indices(ndims) + + for ind in inds: + srcidx = tuple([a[0] for a in ind]) + dstidx = tuple([a[1] for a in ind]) + + yield _check_assignment, srcidx, dstidx |