[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