summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornjsmith <njs@pobox.com>2012-07-11 13:59:43 -0700
committernjsmith <njs@pobox.com>2012-07-11 13:59:43 -0700
commit0920bede09b4a467c9c4e977607c38fd81371087 (patch)
treee8e046c4b829ba3e689220c0abcdbc55c66f789f
parentf4c47a5e366d39f225a19db78010d9785dd801de (diff)
parentd2c8843f0ec8a3b2f81b062b346a94ed5331d4a3 (diff)
downloadnumpy-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.src14
-rw-r--r--numpy/core/tests/test_multiarray_assignment.py78
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