X Tutup
Skip to content

Commit 1351c1d

Browse files
committed
Implementation of PyBuffer.copyFrom dealing correctly with overlap.
Fixes regression in test_memoryview due to recent refactoring. JUnit test provided explicitly for copyFrom() sliced versions of self.
1 parent c16c117 commit 1351c1d

File tree

5 files changed

+180
-127
lines changed

5 files changed

+180
-127
lines changed

src/org/python/core/buffer/Base1DBuffer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ protected int calcGreatestIndex() {
7777
} else if (stride > 0) {
7878
return index0 + (shape[0] - 1) * stride;
7979
} else {
80-
return index0 - 1;
80+
return index0;
8181
}
8282
}
8383

src/org/python/core/buffer/BaseArrayBuffer.java

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ public void copyTo(int srcIndex, byte[] dest, int destPos, int count)
113113
@Override
114114
public void copyFrom(byte[] src, int srcPos, int destIndex, int count)
115115
throws IndexOutOfBoundsException, PyException {
116+
copyFrom(src, srcPos, 1, destIndex, count);
117+
}
118+
119+
/**
120+
* Generalisation of {@link PyBuffer#copyFrom(byte[], int, int, int)} to allow a stride within
121+
* the source array.
122+
*
123+
* @param src source byte array
124+
* @param srcPos byte-index location in source of first byte to copy
125+
* @param srcStride byte-index increment from one item to the next
126+
* @param destIndex starting item-index in the destination (i.e. <code>this</code>)
127+
* @param count number of items to copy in
128+
* @throws IndexOutOfBoundsException if access out of bounds in source or destination
129+
* @throws PyException (TypeError) if read-only buffer
130+
*/
131+
protected void copyFrom(byte[] src, int srcPos, int srcStride, int destIndex, int count)
132+
throws IndexOutOfBoundsException, PyException {
116133

117134
checkWritable();
118135

@@ -123,16 +140,19 @@ public void copyFrom(byte[] src, int srcPos, int destIndex, int count)
123140
int skip = stride - itemsize;
124141
int d = byteIndex(destIndex);
125142

143+
int srcSkip = srcStride - itemsize;
144+
126145
// Strategy depends on whether items are laid end-to-end or there are gaps
127-
if (skip == 0) {
146+
if (skip == 0 && srcSkip == 0) {
128147
// Straight copy of contiguous bytes
129148
System.arraycopy(src, srcPos, storage, d, count * itemsize);
130149
} else {
131-
// Non-contiguous copy: single byte items
132150
int limit = d + count * stride, s = srcPos;
133151
if (itemsize == 1) {
152+
// Non-contiguous copy: single byte items
134153
for (; d != limit; d += stride) {
135-
storage[d] = src[s++];
154+
storage[d] = src[s];
155+
s += srcStride;
136156
}
137157
} else {
138158
// Non-contiguous copy: itemsize bytes then skip to next item
@@ -141,6 +161,7 @@ public void copyFrom(byte[] src, int srcPos, int destIndex, int count)
141161
while (d < t) {
142162
storage[d++] = src[s++];
143163
}
164+
s += srcSkip;
144165
}
145166
}
146167
}
@@ -149,56 +170,41 @@ public void copyFrom(byte[] src, int srcPos, int destIndex, int count)
149170

150171
@Override
151172
public void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException {
152-
if (src instanceof BaseArrayBuffer) {
173+
if (src instanceof BaseArrayBuffer && !this.overlaps((BaseArrayBuffer)src)) {
174+
// We can do this efficiently, copying between arrays.
153175
copyFromArrayBuffer((BaseArrayBuffer)src);
154176
} else {
155177
super.copyFrom(src);
156178
}
157179
}
158180

181+
private boolean overlaps(BaseArrayBuffer src) {
182+
if (src.storage != this.storage) {
183+
return false;
184+
} else {
185+
int low = calcLeastIndex(), high = calcGreatestIndex();
186+
int srcLow = src.calcLeastIndex(), srcHigh = src.calcGreatestIndex();
187+
return (srcHigh >= low && high >= srcLow);
188+
}
189+
}
190+
159191
private void copyFromArrayBuffer(BaseArrayBuffer src) throws IndexOutOfBoundsException,
160192
PyException {
161193

162-
checkWritable();
163194
src.checkDimension(1);
164195

165196
int itemsize = getItemsize();
166197
int count = getSize();
167198

168-
// Block operation if different item or overall size (permit reshape)
199+
// Block operation if different item or overall size
169200
if (src.getItemsize() != itemsize || src.getSize() != count) {
170201
throw differentStructure();
171202
}
172203

173-
for (int i = 0; i < count; i++) {
174-
int s = src.byteIndex(i), d = byteIndex(i);
175-
for (int j = 0; j < itemsize; j++) {
176-
storage[d++] = src.byteAtImpl(s++);
177-
}
178-
}
204+
// We depend on the striding copyFrom() acting directly on the source storage
205+
copyFrom(src.storage, src.index0, src.strides[0], 0, count);
179206
}
180207

181-
/**
182-
* Copy blocks of bytes, equally spaced in the source array, to locations equally spaced in the
183-
* destination array, which may be the same array. The byte at
184-
* <code>src[srcPos+k*srcStride+j]</code> will be copied to
185-
* <code>dst[dstPos+k*dstStride+j]</code> for <code>0&le;k&lt;count</code> and
186-
* <code>0&le;j&lt;size</code>. When the source and destination are the same array, the method
187-
* deals correctly with the risk that a byte gets written under the alias <code>dst[x]</code>
188-
* before it should have been copied referenced as <code>src[y]</code>.
189-
*
190-
* @param size of the blocks of bytes
191-
* @param src the source array
192-
* @param srcPos the position of the first block in the source
193-
* @param srcStride the interval between the start of each block in the source
194-
* @param dst the destination array
195-
* @param dstPos the position of the first block in the destination
196-
* @param dstStride the interval between the start of each block in the destination
197-
* @param count the number of blocks to copy
198-
*/
199-
private static void slicedArrayCopy(int size, byte[] src, int srcPos, int srcStride,
200-
byte[] dst, int dstPos, int dstStride, int count) {}
201-
202208
@Override
203209
protected ByteBuffer getNIOByteBufferImpl() {
204210
// The buffer spans the whole storage, which may include data not in the view

src/org/python/core/buffer/BaseBuffer.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -536,18 +536,21 @@ public void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException
536536

537537
int itemsize = getItemsize();
538538
int count = getSize();
539+
int byteLen = src.getLen();
539540

540541
// Block operation if different item or overall size (permit reshape)
541-
if (src.getItemsize() != itemsize || src.getLen() != count * itemsize) {
542+
if (src.getItemsize() != itemsize || byteLen != count * itemsize) {
542543
throw differentStructure();
543544
}
544545

545-
// XXX Re-think this using ByteBuffer when the API provides a byteIndex() method
546-
assert itemsize == 1;
547-
// XXX This only moves the first byte of each item
548-
for (int i = 0; i < count; i++) {
549-
storeAt(src.byteAt(i), i);
550-
}
546+
/*
547+
* It is not possible in general to know that this and src do not share storage. There is
548+
* always a risk of incorrect results if we do not go via an intermediate byte array.
549+
* Sub-classes may be able to avoid this.
550+
*/
551+
byte[] t = new byte[byteLen];
552+
src.copyTo(t, 0);
553+
this.copyFrom(t, 0, 0, count);
551554
}
552555

553556
@Override

src/org/python/core/buffer/BaseNIOBuffer.java

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -221,61 +221,6 @@ protected void copyFrom(ByteBuffer src, int dstIndex, int count)
221221
}
222222
}
223223

224-
/**
225-
* {@inheritDoc}
226-
* <p>
227-
* The default implementation in <code>BaseNIOBuffer</code> deals with the general
228-
* one-dimensional case.
229-
*/
230-
@Override
231-
public void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException {
232-
233-
int length = getLen();
234-
int itemsize = getItemsize();
235-
236-
// Valid operation only if writable and same length and itemsize
237-
checkWritable();
238-
if (src.getLen() != length || src.getItemsize() != itemsize) {
239-
throw differentStructure();
240-
}
241-
242-
if (length > 0) {
243-
// Pick up attributes necessary to choose an efficient copy strategy
244-
int stride = getStrides()[0];
245-
int skip = stride - itemsize;
246-
247-
ByteBuffer dst = getNIOByteBuffer();
248-
249-
// Strategy depends on whether destination items are laid end-to-end or there are gaps
250-
if (skip == 0) {
251-
// Straight copy to contiguous bytes
252-
for (int i = 0; i < length; i++) {
253-
dst.put(src.byteAt(i));
254-
}
255-
256-
} else if (itemsize == 1) {
257-
// Non-contiguous copy: single byte items
258-
int pos = dst.position();
259-
for (int i = 0; i < length; i++) {
260-
dst.put(pos, src.byteAt(i));
261-
pos += stride;
262-
}
263-
264-
} else {
265-
// Non-contiguous copy: each time, and itemsize > 1
266-
int pos = dst.position();
267-
int s = 0;
268-
for (int i = 0; i < length; i++) {
269-
for (int j = 0; j < itemsize; j++) {
270-
dst.put(pos++, src.byteAt(s++));
271-
}
272-
pos += skip;
273-
}
274-
}
275-
}
276-
277-
}
278-
279224
@Override
280225
protected ByteBuffer getNIOByteBufferImpl() {
281226
return storage.duplicate();

0 commit comments

Comments
 (0)
X Tutup