[Jython-checkins] jython: Add to PyBuffer API a replacement for Pointer using java.nio.ByteBuffer.
jeff.allen
jython-checkins at python.org
Sat Aug 27 09:12:18 EDT 2016
https://hg.python.org/jython/rev/00f53f67cf47
changeset: 7936:00f53f67cf47
parent: 7918:87534ec6252a
user: Jeff Allen <ja.py at farowl.co.uk>
date: Sat May 07 13:25:44 2016 +0100
summary:
Add to PyBuffer API a replacement for Pointer using java.nio.ByteBuffer.
Pointer is deprecated, and the API uses java.nio.ByteBuffer instead.
Unit tests are created from the Pointer equivalents. Storage is still
implemented as byte[] pending further change.
files:
src/org/python/core/PyBUF.java | 10 +-
src/org/python/core/PyBuffer.java | 43 +-
src/org/python/core/buffer/BaseBuffer.java | 29 +-
src/org/python/core/buffer/SimpleBuffer.java | 12 +-
src/org/python/core/buffer/SimpleStringBuffer.java | 15 +-
src/org/python/core/buffer/Strided1DBuffer.java | 2 +
src/org/python/core/buffer/ZeroByteBuffer.java | 1 +
tests/java/org/python/core/PyBufferTest.java | 240 ++++++++-
8 files changed, 291 insertions(+), 61 deletions(-)
diff --git a/src/org/python/core/PyBUF.java b/src/org/python/core/PyBUF.java
--- a/src/org/python/core/PyBUF.java
+++ b/src/org/python/core/PyBUF.java
@@ -122,7 +122,7 @@
/**
* A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
* specify that it requires {@link PyBuffer#getFormat()} to return a <code>String</code>
- * indicating the type of the unit. This exists for compatibility with CPython, as Jython as the
+ * indicating the type of the unit. This exists for compatibility with CPython, as in Jython the
* format is always provided by <code>getFormat()</code>.
*/
static final int FORMAT = 0x0004;
@@ -203,13 +203,14 @@
* Equivalent to <code>(INDIRECT | WRITABLE | FORMAT)</code>. Also use this in the request if
* you plan only to use the fully-encapsulated API (<code>byteAt</code>, <code>storeAt</code>,
* <code>copyTo</code>, <code>copyFrom</code>, etc.), without ever calling
- * {@link PyBuffer#getBuf()}.
+ * {@link PyBuffer#getNIOByteBuffer()} or using {@link PyBuffer#Pointer()}.
*/
static final int FULL = INDIRECT | WRITABLE | FORMAT;
/**
* Equivalent to <code>(INDIRECT | FORMAT)</code>. Also use this in the request if you plan only
* to use the fully-encapsulated API (<code>byteAt</code>, <code>copyTo</code>, etc.), read
- * only, without ever calling {@link PyBuffer#getBuf()}.
+ * only, without ever calling {@link PyBuffer#getNIOByteBuffer()} or using
+ * {@link PyBuffer#Pointer()}.
*/
static final int FULL_RO = INDIRECT | FORMAT;
@@ -221,7 +222,8 @@
* through the purely abstract part of the API). <code>getBuffer</code> will raise an exception
* if the exporter cannot expose its storage as Java array.
*/
- static final int AS_ARRAY = 0x10000000;
+ // XXX Pending: @Deprecated
+ static final int AS_ARRAY = 0x10000000;
/* Constants for readability, not standard for CPython */
diff --git a/src/org/python/core/PyBuffer.java b/src/org/python/core/PyBuffer.java
--- a/src/org/python/core/PyBuffer.java
+++ b/src/org/python/core/PyBuffer.java
@@ -244,8 +244,11 @@
/**
* Obtain a {@link java.nio.ByteBuffer} giving access to the bytes that hold the data being
- * exported to the consumer. For a one-dimensional contiguous buffer, assuming the following
- * client code where <code>obj</code> has type <code>BufferProtocol</code>:
+ * exported to the consumer. The position of the buffer is at the item with zero index, the
+ * limit of the buffer is one beyond the largest valid index, and the mark is undefined.
+ * <p>
+ * For a one-dimensional contiguous buffer, assuming the following client code where
+ * <code>obj</code> has type <code>BufferProtocol</code>:
*
* <pre>
* PyBuffer a = obj.getBuffer(PyBUF.SIMPLE);
@@ -253,11 +256,13 @@
* ByteBuffer bb = a.getNIOBuffer();
* </pre>
*
- * the item with index <code>bb.pos()+k</code> is in the buffer <code>bb</code> at positions
+ * the item with index <code>k</code> is in <code>bb</code> at positions
* <code>bb.pos()+k*itemsize</code> to <code>bb.pos()+(k+1)*itemsize - 1</code> inclusive. And
* if <code>itemsize==1</code>, the item is simply the byte at position <code>bb.pos()+k</code>.
* The buffer limit is set to the first byte beyond the valid data. A block read or write will
- * therefore access the contents sequentially.
+ * therefore access the contents sequentially. In a one-dimensional contiguous buffer (only) it
+ * is safe to rely on <code>bb.remaining()</code> to obtain the number of bytes representing the
+ * object state.
* <p>
* If the buffer is multidimensional or non-contiguous (strided), the buffer position is still
* the (first byte of) the item at index <code>[0]</code> or <code>[0,...,0]</code>, and the
@@ -265,11 +270,35 @@
* using the <code>shape</code>, <code>strides</code> and maybe <code>suboffsets</code> provided
* by the API.
*
- * @return a ByteBuffer equivalent to the exported data contents.
+ * @return a ByteBuffer onto the exported data contents.
*/
ByteBuffer getNIOByteBuffer();
- // Direct access to actual storage
+ /**
+ * Obtain a {@link java.nio.ByteBuffer} giving access to the bytes that hold the data being
+ * exported to the consumer, and positioned at a particular index.
+ * <p>
+ * Essentially this saves the client from computing the byte offset of a particular index. The
+ * client is free to navigate the underlying byte data through the <code>ByteBuffer</code>.
+ *
+ * @param index in the buffer to position the pointer
+ * @return a ByteBuffer onto the exported data contents, positioned at the indexed item.
+ */
+ ByteBuffer getNIOByteBuffer(int index);
+
+ /**
+ * Obtain a {@link java.nio.ByteBuffer} giving access to the bytes that hold the data being
+ * exported to the consumer, and positioned at a particular multi-dimensional index.
+ * <p>
+ * Essentially this saves the client from computing the byte offset of a particular index. The
+ * client is free to navigate the underlying byte data through the <code>ByteBuffer</code>.
+ *
+ * @param indices multidimensional index at which to position the pointer
+ * @return a ByteBuffer onto the exported data contents, positioned at the indexed item.
+ */
+ ByteBuffer getNIOByteBuffer(int... indices);
+
+ // Direct access to actual storage (deprecated)
//
/**
@@ -279,6 +308,7 @@
*
* @return true if array access is supported, false if it is not.
*/
+ // XXX Pending: @Deprecated
boolean hasArray();
/**
@@ -288,6 +318,7 @@
* this array, and in others not. See {@link PyBuffer#isReadonly()}. It is used by the Jython
* buffer API roughly where the CPython buffer API uses a C (char *) pointer.
*/
+ @Deprecated
public static class Pointer {
/** Reference to the array holding the bytes. */
diff --git a/src/org/python/core/buffer/BaseBuffer.java b/src/org/python/core/buffer/BaseBuffer.java
--- a/src/org/python/core/buffer/BaseBuffer.java
+++ b/src/org/python/core/buffer/BaseBuffer.java
@@ -65,6 +65,7 @@
* (contiguous cases) or the index in <code>storage</code> that should be treated as the item
* with index zero (retrieved say by {@link #byteAt(int)}).
*/
+ // XXX Pending change of implementation to ByteBuffer
protected byte[] storage;
/**
@@ -600,23 +601,43 @@
@Override
public ByteBuffer getNIOByteBuffer() {
- // Determine the limit of the buffer just beyond the last item.
- int length = calcGreatestIndex() + getItemsize() - index0;
- ByteBuffer b = ByteBuffer.wrap(storage, index0, length);
+ // The buffer spans the whole storage, which may include data not in the view
+ ByteBuffer b = ByteBuffer.wrap(storage);
+ b.limit(calcGreatestIndex() + getItemsize()).position(index0);
// Return as read-only if it is.
return isReadonly() ? b.asReadOnlyBuffer() : b;
}
@Override
+ public ByteBuffer getNIOByteBuffer(int index) {
+ // The buffer spans the whole storage but is positioned at index
+ ByteBuffer b = getNIOByteBuffer();
+ b.position(calcIndex(index));
+ return b;
+ }
+
+ @Override
+ public ByteBuffer getNIOByteBuffer(int... indices) {
+ // The buffer spans the whole storage but is positioned at indices[]
+ ByteBuffer b = getNIOByteBuffer();
+ b.position(calcIndex(indices));
+ // Return as read-only if it is.
+ return b;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
public Pointer getBuf() {
return new Pointer(storage, index0);
}
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int index) throws IndexOutOfBoundsException {
return new Pointer(storage, calcIndex(index));
}
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int... indices) throws IndexOutOfBoundsException {
return new Pointer(storage, calcIndex(indices));
@@ -664,7 +685,7 @@
/**
* Some <code>PyBuffer</code>s, those created by slicing a <code>PyBuffer</code> are related to
* a root <code>PyBuffer</code>. During creation of such a slice, we need to supply a value for
- * this root. If the present object is not itself a slice, this is root is the object itself; if
+ * this root. If the present object is not itself a slice, this root is the object itself; if
* the buffer is already a slice, it is the root it was given at creation time. Often this is
* the only difference between a slice-view and a directly-exported buffer. Override this method
* in slices to return the root buffer of the slice.
diff --git a/src/org/python/core/buffer/SimpleBuffer.java b/src/org/python/core/buffer/SimpleBuffer.java
--- a/src/org/python/core/buffer/SimpleBuffer.java
+++ b/src/org/python/core/buffer/SimpleBuffer.java
@@ -50,8 +50,7 @@
* @throws ArrayIndexOutOfBoundsException if <code>index0</code> and <code>size</code> are
* inconsistent with <code>storage.length</code>
*/
- // XXX: "for sub-class use" = should be protected?
- public SimpleBuffer(byte[] storage, int index0, int size) throws PyException,
+ protected SimpleBuffer(byte[] storage, int index0, int size) throws PyException,
ArrayIndexOutOfBoundsException {
this();
this.storage = storage; // Exported data
@@ -230,18 +229,13 @@
}
}
- @Override
- public ByteBuffer getNIOByteBuffer() {
- // Simplify for one-dimensional contiguous bytes
- ByteBuffer b = ByteBuffer.wrap(storage, index0, shape[0]);
- return isReadonly() ? b.asReadOnlyBuffer() : b;
- }
-
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int index) throws IndexOutOfBoundsException {
return new Pointer(storage, index0 + index);
}
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int... indices) throws IndexOutOfBoundsException {
checkDimension(indices.length);
diff --git a/src/org/python/core/buffer/SimpleStringBuffer.java b/src/org/python/core/buffer/SimpleStringBuffer.java
--- a/src/org/python/core/buffer/SimpleStringBuffer.java
+++ b/src/org/python/core/buffer/SimpleStringBuffer.java
@@ -8,11 +8,11 @@
/**
* Buffer API that appears to be a one-dimensional array of one-byte items providing read-only API,
* but which is actually backed by a Java String. Some of the buffer API absolutely needs access to
- * the data as a byte array (those parts that involve a <code>PyBuffer.Pointer</code> result), and
- * therefore this class must create a byte array from the String for them. However, it defers
- * creation of a byte array until that part of the API is actually used. Where possible, this class
- * overrides those methods in SimpleBuffer that would otherwise access the byte array attribute to
- * use the String instead.
+ * the data as a byte array (those parts that involve a {@link java.nio.ByteBuffer} or
+ * {@link PyBuffer.Pointer} result), and therefore this class must create a byte array from the
+ * String for them. However, it defers creation of a byte array until that part of the API is
+ * actually used. Where possible, this class overrides those methods in SimpleBuffer that would
+ * otherwise access the byte array attribute to use the String instead.
*/
public class SimpleStringBuffer extends SimpleBuffer {
@@ -124,7 +124,7 @@
public ByteBuffer getNIOByteBuffer() {
// Force creation of the actual byte array from the String.
ensureHaveBytes();
- return super.getNIOByteBuffer().asReadOnlyBuffer();
+ return super.getNIOByteBuffer();
}
/**
@@ -142,6 +142,7 @@
* <p>
* This method creates an actual byte array from the underlying String if none yet exists.
*/
+ @SuppressWarnings("deprecation")
@Override
public Pointer getBuf() {
ensureHaveBytes();
@@ -153,6 +154,7 @@
* <p>
* This method creates an actual byte array from the underlying String if none yet exists.
*/
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int index) {
ensureHaveBytes();
@@ -164,6 +166,7 @@
* <p>
* This method creates an actual byte array from the underlying String if none yet exists.
*/
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int... indices) {
ensureHaveBytes();
diff --git a/src/org/python/core/buffer/Strided1DBuffer.java b/src/org/python/core/buffer/Strided1DBuffer.java
--- a/src/org/python/core/buffer/Strided1DBuffer.java
+++ b/src/org/python/core/buffer/Strided1DBuffer.java
@@ -224,11 +224,13 @@
}
}
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int index) {
return new Pointer(storage, index0 + index * stride);
}
+ @SuppressWarnings("deprecation")
@Override
public Pointer getPointer(int... indices) {
// BaseBuffer implementation can be simplified since if indices.length!=1 we error.
diff --git a/src/org/python/core/buffer/ZeroByteBuffer.java b/src/org/python/core/buffer/ZeroByteBuffer.java
--- a/src/org/python/core/buffer/ZeroByteBuffer.java
+++ b/src/org/python/core/buffer/ZeroByteBuffer.java
@@ -135,6 +135,7 @@
* <p>
* The implementation in <code>ZeroByteBuffer</code> efficiently returns an empty buffer.
*/
+ @SuppressWarnings("deprecation")
@Override
public Pointer getBuf() {
// Has to be new because the client is allowed to manipulate the contents.
diff --git a/tests/java/org/python/core/PyBufferTest.java b/tests/java/org/python/core/PyBufferTest.java
--- a/tests/java/org/python/core/PyBufferTest.java
+++ b/tests/java/org/python/core/PyBufferTest.java
@@ -3,6 +3,7 @@
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
@@ -959,10 +960,108 @@
byte[] result = bytesFromByteAt(sliceView);
assertBytesEqual(" testGetBufferSliceWithStride failure: ", expected, result);
}
+ /**
+ * Test method for {@link org.python.core.PyBuffer#getNIOByteBuffer()}.
+ */
+ public void testGetNIOByteBuffer() {
+ for (BufferTestPair test : buffersToRead) {
+ if (verbosity > 0) {
+ System.out.println("getNIOByteBuffer: " + test);
+ }
+ int stride = test.strides[0];
+
+ if (stride == 1) {
+
+ // The client should not have to support navigation with the strides array
+ int flags = test.readonly ? PyBUF.SIMPLE : PyBUF.SIMPLE + PyBUF.WRITABLE;
+ PyBuffer view = test.subject.getBuffer(flags);
+
+ ByteBuffer bp = view.getNIOByteBuffer();
+ assertBytesEqual("buffer does not match reference", test.material.bytes, bp);
+
+ } else {
+
+ // The client will have to navigate with the strides array
+ int flags = test.readonly ? PyBUF.STRIDED_RO : PyBUF.STRIDED;
+ PyBuffer view = test.subject.getBuffer(flags);
+
+ stride = view.getStrides()[0]; // Just possibly != test.strides when length<=1
+ ByteBuffer bp = view.getNIOByteBuffer();
+ assertBytesEqual("buffer does not match reference", test.material.bytes, bp, stride);
+ }
+
+ }
+ }
+
+ /**
+ * Test method for {@link org.python.core.PyBuffer#getNIOByteBuffer(int)}.
+ */
+ public void testGetNIOByteBuffer_int() {
+ for (BufferTestPair test : buffersToRead) {
+ if (verbosity > 0) {
+ System.out.println("getNIOByteBuffer(int): " + test);
+ }
+ PyBuffer view = test.view;
+ int n = test.material.length, itemsize = view.getItemsize();
+ byte[] exp = new byte[itemsize], bytes = test.material.bytes;
+
+ for (int i = 0; i < n; i++) {
+ // Expected result is one item (allow for itemsize)
+ int p = i * itemsize;
+ for (int j = 0; j < itemsize; j++) {
+ exp[j] = bytes[p + j];
+ }
+
+ // Get buffer and check for correct data at bb.position()
+ ByteBuffer bb = view.getNIOByteBuffer(i);
+ assertBytesEqual("getNIOByteBuffer(int) value", exp, bb);
+ }
+ }
+ }
+
+ /**
+ * Test method for {@link org.python.core.PyBuffer#getNIOByteBuffer(int[])}.
+ */
+ public void testGetNIOByteBuffer_intArray() {
+ int[] index = new int[1];
+ for (BufferTestPair test : buffersToRead) {
+ if (verbosity > 0) {
+ System.out.println("getNIOByteBuffer(int[]): " + test);
+ }
+ PyBuffer view = test.view;
+ int n = test.material.length, itemsize = view.getItemsize();
+ byte[] exp = new byte[itemsize], bytes = test.material.bytes;
+
+ for (int i = 0; i < n; i++) {
+ // Expected result is one item (allow for itemsize)
+ int p = i * itemsize;
+ for (int j = 0; j < itemsize; j++) {
+ exp[j] = bytes[p + j];
+ }
+
+ // Get buffer and check for correct data at bb.position()
+ index[0] = i;
+ ByteBuffer bb = view.getNIOByteBuffer(index);
+ assertBytesEqual("getNIOByteBuffer(int[]) value", exp, bb);
+ }
+
+ // Check 2D index throws
+ try {
+ view.getNIOByteBuffer(0, 0);
+ fail("Use of 2D index did not raise exception");
+ } catch (PyException pye) {
+ // Expect BufferError
+ assertEquals(Py.BufferError, pye.type);
+ }
+ }
+ }
+
+
/**
* Test method for {@link org.python.core.PyBuffer#getBuf()}.
*/
+ @SuppressWarnings("deprecation")
public void testGetBuf() {
for (BufferTestPair test : buffersToRead) {
if (verbosity > 0) {
@@ -980,6 +1079,7 @@
assertBytesEqual("buffer does not match reference", test.material.bytes, bp);
} else {
+
// The client will have to navigate with the strides array
int flags = test.readonly ? PyBUF.STRIDED_RO : PyBUF.STRIDED;
PyBuffer view = test.subject.getBuffer(flags);
@@ -995,6 +1095,7 @@
/**
* Test method for {@link org.python.core.PyBuffer#getPointer(int)}.
*/
+ @SuppressWarnings("deprecation")
public void testGetPointer() {
for (BufferTestPair test : buffersToRead) {
if (verbosity > 0) {
@@ -1021,6 +1122,7 @@
/**
* Test method for {@link org.python.core.PyBuffer#getPointer(int[])}.
*/
+ @SuppressWarnings("deprecation")
public void testGetPointerNdim() {
int[] index = new int[1];
for (BufferTestPair test : buffersToRead) {
@@ -1458,49 +1560,121 @@
}
/**
- * Customised assert method comparing a buffer pointer to a byte array, usually the one from
- * ByteMaterial.
+ * Custom assert method comparing the bytes at a {@link PyBuffer.Pointer} to those in a byte
+ * array, when that <code>Pointer</code> is obtained from a contiguous <code>PyBuffer</code>.
+ * Let <code>bp[i]</code> denote <code>bp.storage[bp.offset+i]</code>, by analogy with a C
+ * pointer. It is required that <code>bp[k] == expected[k]</code>, for every index in
+ * <code>expected</code>. If not, a <code>fail()</code> is declared.
*
* @param message to issue on failure
* @param expected expected byte array
* @param bp result to test
*/
+ @SuppressWarnings("deprecation")
static void assertBytesEqual(String message, byte[] expected, PyBuffer.Pointer bp) {
assertBytesEqual(message, expected, bp, 1);
}
/**
- * Customised assert method comparing a buffer pointer to a byte array, usually the one from
- * ByteMaterial.
+ * Custom assert method comparing the bytes in an NIO {@link ByteBuffer} to those in a byte
+ * array, when that <code>ByteBuffer</code> is obtained from a contiguous <code>PyBuffer</code>.
+ * Let <code>bb[i]</code> denote <code>bb.get(bb.position()+i)</code>, by analogy with a C
+ * pointer. It is required that <code>bb[k] == expected[k]</code>, for every index
+ * <code>k</code> in <code>expected</code>. If not, a <code>fail()</code> is declared.
+ *
+ * @param message to issue on failure
+ * @param expected expected byte array
+ * @param bb result to test
+ */
+ static void assertBytesEqual(String message, byte[] expected, ByteBuffer bb) {
+ // Use the position-advancing buffer get()
+ byte[] actual = new byte[expected.length];
+ assertEquals( message + " (size in buffer)", expected.length, bb.remaining());
+ bb.get(actual);
+ assertBytesEqual(message, expected, actual);
+ }
+
+ /**
+ * Custom assert method comparing the bytes at a {@link PyBuffer.Pointer} to those in a byte
+ * array, when that <code>Pointer</code> is obtained from a striding <code>PyBuffer</code>. Let
+ * <code>bp[i]</code> denote <code>bp.storage[bp.offset+i]</code>, by analogy with a C pointer.
+ * It is required that <code>bp[k*stride] == expected[k]</code>, for every index <code>k</code>
+ * in <code>expected</code>. If not, a <code>fail()</code> is declared.
*
* @param message to issue on failure
* @param expected expected byte array
* @param bp result to test
- * @param stride in the storage array
+ * @param stride in the <code>bp.storage</code> array
*/
+ @SuppressWarnings("deprecation")
static void assertBytesEqual(String message, byte[] expected, PyBuffer.Pointer bp, int stride) {
assertBytesEqual(message, expected, 0, expected.length, bp.storage, bp.offset, stride);
}
/**
- * Customised assert method comparing a buffer pointer to a byte array, usually the one from
- * ByteMaterial.
+ * Custom assert method comparing the bytes in an NIO {@link ByteBuffer} to those in a byte
+ * array, when that <code>ByteBuffer</code> is obtained from a striding <code>PyBuffer</code>.
+ * Let <code>bb[i]</code> denote <code>bb.get(bb.position()+i)</code>, by analogy with a C
+ * pointer. It is required that <code>bb[k*stride] == expected[k]</code>, for every index
+ * <code>k</code> in <code>expected</code>. If not, a <code>fail()</code> is declared.
*
* @param message to issue on failure
* @param expected expected byte array
- * @param expectedStart where to start the comparison in expected
+ * @param bb result to test
+ * @param stride in the buffer <code>bb</code>
+ */
+ static void assertBytesEqual(String message, byte[] expected, ByteBuffer bb, int stride) {
+ assertBytesEqual(message, expected, 0, expected.length, bb, stride);
+ }
+
+ /**
+ * Custom assert method comparing the bytes at a {@link PyBuffer.Pointer} to those in a byte
+ * array, when that <code>Pointer</code> is obtained from a striding <code>PyBuffer</code>. Let
+ * <code>bp[i]</code> denote <code>bp.storage[bp.offset+i]</code>, by analogy with a C pointer.
+ * It is required that <code>bp[k*stride] == expected[expectedStart+k]</code>, for
+ * <code>k=0</code> to <code>n-1</code>. If not, a <code>fail()</code> is declared.
+ *
+ * @param message to issue on failure
+ * @param expected expected byte array
+ * @param expectedStart where to start the comparison in <code>expected</code>
* @param n number of bytes to test
- * @param bb result to test
- * @param stride in the storage array
+ * @param bp result to test
+ * @param stride in the <code>bp.storage</code> array
*/
+ @SuppressWarnings("deprecation")
static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n,
PyBuffer.Pointer bp, int stride) {
assertBytesEqual(message, expected, expectedStart, n, bp.storage, bp.offset, stride);
}
/**
- * Customised assert method comparing a byte arrays: values in the actual value must match all
- * those in expected[], and they must be the same length.
+ * Custom assert method comparing the bytes in an NIO {@link ByteBuffer} to those in a byte
+ * array, when that <code>ByteBuffer</code> is obtained from a striding <code>PyBuffer</code>.
+ * Let <code>bb[i]</code> denote <code>bb.get(bb.position()+i)</code>, by analogy with a C
+ * pointer. It is required that <code>bb[k*stride] == expected[expectedStart+k]</code>, for
+ * <code>k=0</code> to <code>n-1</code>. If not, a <code>fail()</code> is declared.
+ *
+ * @param message to issue on failure
+ * @param expected expected byte array
+ * @param expectedStart where to start the comparison in <code>expected</code>
+ * @param n number of bytes to test
+ * @param bb result to test
+ * @param stride in the buffer <code>bb</code>
+ */
+ static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n,
+ ByteBuffer bb, int stride) {
+ // Note that this approach leaves the buffer position unmodified
+ int p = bb.position();
+ byte[] actual = new byte[n];
+ for (int k = 0; k < n; k++, p += stride) {
+ actual[k] = bb.get(p);
+ }
+ assertBytesEqual(message, expected, expectedStart, n, actual, 0);
+ }
+
+ /**
+ * Custom assert method comparing byte arrays: values in <code>actual[]</code> must match all
+ * those in <code>expected[]</code>, and they must be the same length.
*
* @param message to issue on failure
* @param expected expected byte array
@@ -1512,28 +1686,30 @@
}
/**
- * Customised assert method comparing byte arrays: values in the actual value starting at
- * actual[actualStart] must match all those in expected[], and there must be enough of them.
+ * Custom assert method comparing byte arrays. It is required that
+ * <code>actual[k] == expected[k]</code>, for <code>k=0</code> to <code>expected.length-1</code>
+ * . If not, a <code>fail()</code> is declared.
*
* @param message to issue on failure
* @param expected expected byte array
* @param actual result to test
- * @param actualStart where to start the comparison in actual
+ * @param actualStart where to start the comparison in <code>actual</code>
*/
static void assertBytesEqual(String message, byte[] expected, byte[] actual, int actualStart) {
assertBytesEqual(message, expected, 0, expected.length, actual, actualStart, 1);
}
/**
- * Customised assert method comparing byte arrays: values starting at actual[actualStart] must
- * those starting at expected[expectedStart], for a distance of n bytes.
+ * Custom assert method comparing byte arrays. It is required that
+ * <code>actual[actualStart+k] == expected[expectedStart+k]</code>, for <code>k=0</code> to
+ * <code>n-1</code>. If not, a <code>fail()</code> is declared.
*
* @param message to issue on failure
* @param expected expected byte array
- * @param expectedStart where to start the comparison in expected
+ * @param expectedStart where to start the comparison in <code>expected</code>
* @param n number of bytes to test
* @param actual result to test
- * @param actualStart where to start the comparison in actual
+ * @param actualStart where to start the comparison in <code>actual</code>
*/
static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n,
byte[] actual, int actualStart) {
@@ -1541,16 +1717,17 @@
}
/**
- * Customised assert method comparing byte arrays: values starting at actual[actualStart] must
- * those starting at expected[expectedStart], for a distance of n bytes.
+ * Custom assert method comparing byte arrays. It is required that
+ * <code>actual[actualStart+k*stride] == expected[expectedStart+k]</code>, for
+ * <code>k=0</code> to <code>n-1</code>. If not, a <code>fail()</code> is declared.
*
* @param message to issue on failure
* @param expected expected byte array
- * @param expectedStart where to start the comparison in expected
+ * @param expectedStart where to start the comparison in <code>expected</code>
* @param n number of bytes to test
* @param actual result to test
- * @param actualStart where to start the comparison in actual
- * @param stride spacing of bytes in actual array
+ * @param actualStart where to start the comparison in <code>actual</code>
+ * @param stride spacing of bytes in <code>actual</code> array
*/
static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n,
byte[] actual, int actualStart, int stride) {
@@ -1579,13 +1756,10 @@
// If we stopped early, diagnose the problem
if (j < jLimit) {
- System.out.println(" expected:"
- + Arrays.toString(Arrays.copyOfRange(expected, expectedStart, expectedStart
- + n)));
- System.out
- .println(" actual:"
- + Arrays.toString(Arrays.copyOfRange(actual, actualStart,
- actualStart + n)));
+ byte[] a = Arrays.copyOfRange(actual, actualStart, actualStart + n);
+ byte[] e = Arrays.copyOfRange(expected, expectedStart, expectedStart + n);
+ System.out.println(" expected:" + Arrays.toString(e));
+ System.out.println(" actual:" + Arrays.toString(a));
System.out.println(" _actual_:" + Arrays.toString(actual));
fail(message + " (byte at " + j + ")");
}
@@ -1771,7 +1945,9 @@
@Override
public String toString() {
- int offset = view.getBuf().offset;
+ int offset0 = view.getBuf().offset; // XXX -> view.getNIOByteBuffer().position() ?
+ int offset = view.getNIOByteBuffer().position();
+ assertEquals(offset0, offset);
String offsetSpec = offset > 0 ? "[0@(" + offset + "):" : "[:";
int stride = strides[0];
String sliceSpec = offsetSpec + shape[0] + (stride != 1 ? "*(" + stride + ")]" : "]");
--
Repository URL: https://hg.python.org/jython
More information about the Jython-checkins
mailing list