[Jython-checkins] jython: Add PyBuffer.byteIndex methods.

jeff.allen jython-checkins at python.org
Sat Aug 27 09:12:25 EDT 2016


https://hg.python.org/jython/rev/1cb1ab1e53f3
changeset:   7946:1cb1ab1e53f3
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Thu Jun 30 10:02:08 2016 +0100
summary:
  Add PyBuffer.byteIndex methods.

Allow buffers to provide their indexing polynomial. This makes
unnecessary methods returning a Pointer or ByteBuffer ready-positioned
at a particular index. Minor impacts throughout the buffer
implementations.

files:
  src/org/python/core/PyBuffer.java                  |  55 +++++----
  src/org/python/core/buffer/BaseArrayBuffer.java    |   2 +-
  src/org/python/core/buffer/BaseBuffer.java         |  53 +-------
  src/org/python/core/buffer/BaseNIOBuffer.java      |  15 +-
  src/org/python/core/buffer/SimpleBuffer.java       |   2 +-
  src/org/python/core/buffer/SimpleNIOBuffer.java    |   2 +-
  src/org/python/core/buffer/SimpleStringBuffer.java |   5 +-
  src/org/python/core/buffer/Strided1DBuffer.java    |   2 +-
  src/org/python/core/buffer/Strided1DNIOBuffer.java |   2 +-
  src/org/python/core/buffer/ZeroByteBuffer.java     |   4 +-
  tests/java/org/python/core/PyBufferTest.java       |  53 ---------
  11 files changed, 56 insertions(+), 139 deletions(-)


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
@@ -238,10 +238,39 @@
      */
     public PyBuffer getBufferSlice(int flags, int start, int count, int stride);
 
-    // java.nio access to actual storage
+    // Access to underlying byte-oriented storage
     //
 
     /**
+     * Convert an item index (for a one-dimensional buffer) to an absolute byte index in the storage
+     * shared by the exporter. The storage exported as a <code>PyBuffer</code> is a linearly-indexed
+     * sequence of bytes, although it may not actually be a heap-allocated Java <code>byte[]</code>
+     * object. The purpose of this method is to allow the exporter to define the relationship
+     * between the item index (as used in {@link #byteAt(int)}) and the byte-index (as used with the
+     * <code>ByteBuffer</code> returned by {@link #getNIOByteBuffer()}). See
+     * {@link #byteIndex(int[])} for discussion of the multi-dimensional case.
+     *
+     * @param index item-index from consumer
+     * @return corresponding byte-index in actual storage
+     */
+    // Should it throw IndexOutOfBoundsException if the index <0 or ≥<code>shape[0]</code?
+    int byteIndex(int index) throws IndexOutOfBoundsException;
+
+    /**
+     * Convert a multi-dimensional item index to an absolute byte index in the storage shared by the
+     * exporter. The storage exported as a <code>PyBuffer</code> is a linearly-indexed sequence of
+     * bytes, although it may not actually be a heap-allocated Java <code>byte[]</code> object. The
+     * purpose of this method is to allow the exporter to define the relationship between the item
+     * index (as used in {@link #byteAt(int...)} and the byte-index (as used with the
+     * <code>ByteBuffer</code> returned by {@link #getNIOByteBuffer()}).
+     *
+     * @param indices n-dimensional item-index from consumer
+     * @return corresponding byte-index in actual storage
+     */
+    // Should it throw IndexOutOfBoundsException if any index <0 or ≥<code>shape[i]</code>?
+    int byteIndex(int... indices);
+
+    /**
      * Obtain a {@link java.nio.ByteBuffer} giving access to the bytes that hold the data being
      * exported by the original object. The position of the buffer is at the first byte of the item
      * with zero index (quite possibly not the lowest valid byte-index), the limit of the buffer is
@@ -271,30 +300,6 @@
     ByteBuffer getNIOByteBuffer();
 
     /**
-     * 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);
-
-    /**
      * Report whether the exporter is able to offer direct access to the exported storage as a Java
      * byte array (through the API that involves class {@link Pointer}), or only supports the
      * abstract API. See also {@link PyBUF#AS_ARRAY}.
diff --git a/src/org/python/core/buffer/BaseArrayBuffer.java b/src/org/python/core/buffer/BaseArrayBuffer.java
--- a/src/org/python/core/buffer/BaseArrayBuffer.java
+++ b/src/org/python/core/buffer/BaseArrayBuffer.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected int byteIndex(int... indices) throws IndexOutOfBoundsException {
+    public int byteIndex(int... indices) throws IndexOutOfBoundsException {
         // BaseBuffer implementation can be simplified since if indices.length!=1 we error.
         checkDimension(indices.length); // throws if != 1
         return byteIndex(indices[0]);
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
@@ -351,16 +351,13 @@
         storeAtImpl(value, byteIndex(indices));
     }
 
-    /**
-     * Convert an item index (for a one-dimensional buffer) to a checked absolute byte index in the
-     * actual storage being shared by the exporter. See {@link #byteIndex(int[])} for discussion.
-     *
-     * @param index item-index from consumer
-     * @return corresponding byte-index in actual storage
-     * @throws IndexOutOfBoundsException if the index <0 or ≥<code>shape[0]</code>
+    /*
+     * In this implementation, we throw IndexOutOfBoundsException if index < 0 or > shape[0], but we
+     * could rely on the array or ByteBuffer checks when indexing, especially the latter since
+     * position is checked against limit.
      */
-    // XXX Consider making this part of the PyBuffer interface
-    protected int byteIndex(int index) throws IndexOutOfBoundsException {
+    @Override
+    public int byteIndex(int index) throws IndexOutOfBoundsException {
         // Treat as one-dimensional
         if (index < 0 || index >= shape[0]) {
             throw new IndexOutOfBoundsException();
@@ -368,25 +365,11 @@
         return index0 + index * strides[0];
     }
 
-    /**
-     * Convert a multi-dimensional item index (if we are not using indirection) to an absolute byte
-     * index in the actual storage array being shared by the exporter. The purpose of this method is
-     * to allow a sub-class to define, in one place, an indexing calculation that maps the index as
-     * provided by the consumer into an index in the storage known to the buffer.
-     * <p>
-     * In the usual case where the storage is referenced via a <code>storage</code> member and
-     * {@link #index0} member, the buffer implementation may use <code>storage[byteIndex(i)]</code>
-     * to reference the (first byte of) the item x[i]. This is what the default implementation of
-     * accessors in <code>BaseArrayBuffer</code> will do. In the simplest cases, calling
-     * <code>byteIndex</code> may be an overhead to avoid, and an implementation will specialise the
-     * accessors. The default implementation here is suited to N-dimensional arrays.
-     *
-     * @param indices n-dimensional item-index from consumer
-     * @return corresponding byte-index in actual storage
-     * @throws IndexOutOfBoundsException if any index <0 or ≥<code>shape[i]</code>
+    /*
+     * In this implementation, we throw IndexOutOfBoundsException if any index[i] < 0 or > shape[i].
      */
-    // XXX Consider making this part of the PyBuffer interface
-    protected int byteIndex(int... indices) throws IndexOutOfBoundsException {
+    @Override
+    public int byteIndex(int... indices) throws IndexOutOfBoundsException {
         final int N = checkDimension(indices);
         // In general: index0 + sum(k=0,N-1) indices[k]*strides[k]
         int index = index0;
@@ -653,22 +636,6 @@
     }
 
     @Override
-    public ByteBuffer getNIOByteBuffer(int index) {
-        // The buffer spans the whole storage but is positioned at index
-        ByteBuffer b = getNIOByteBufferImpl();
-        b.position(byteIndex(index));
-        return b;
-    }
-
-    @Override
-    public ByteBuffer getNIOByteBuffer(int... indices) {
-        // The buffer spans the whole storage but is positioned at indices[]
-        ByteBuffer b = getNIOByteBufferImpl();
-        b.position(byteIndex(indices));
-        return b;
-    }
-
-    @Override
     public boolean hasArray() {
         // AS_ARRAY is a non-navigation flag, so is inverted in gFeatureFlags
         return (gFeatureFlags & AS_ARRAY) == 0; // i.e. featureFlags & AS_ARRAY is true
diff --git a/src/org/python/core/buffer/BaseNIOBuffer.java b/src/org/python/core/buffer/BaseNIOBuffer.java
--- a/src/org/python/core/buffer/BaseNIOBuffer.java
+++ b/src/org/python/core/buffer/BaseNIOBuffer.java
@@ -78,7 +78,7 @@
     }
 
     @Override
-    protected int byteIndex(int... indices) throws IndexOutOfBoundsException {
+    public int byteIndex(int... indices) throws IndexOutOfBoundsException {
         // BaseBuffer implementation can be simplified since if indices.length!=1 we error.
         checkDimension(indices.length); // throws if != 1
         return byteIndex(indices[0]);
@@ -123,7 +123,8 @@
 
         if (count > 0) {
 
-            ByteBuffer src = getNIOByteBuffer(srcIndex);
+            ByteBuffer src = getNIOByteBuffer();
+            int pos = byteIndex(srcIndex);
 
             // Pick up attributes necessary to choose an efficient copy strategy
             int itemsize = getItemsize();
@@ -132,12 +133,11 @@
             // Strategy depends on whether items are laid end-to-end contiguously or there are gaps
             if (stride == itemsize) {
                 // stride == itemsize: straight copy of contiguous bytes
-                src.limit(src.position() + count * itemsize);
+                src.limit(pos + count * itemsize).position(pos);
                 dest.put(src);
 
             } else if (itemsize == 1) {
                 // Non-contiguous copy: single byte items
-                int pos = src.position();
                 for (int i = 0; i < count; i++) {
                     src.position(pos);
                     dest.put(src.get());
@@ -146,7 +146,6 @@
 
             } else {
                 // Non-contiguous copy: each time, copy itemsize bytes then skip
-                int pos = src.position();
                 for (int i = 0; i < count; i++) {
                     src.limit(pos + itemsize).position(pos);
                     dest.put(src);
@@ -184,7 +183,8 @@
 
         if (count > 0) {
 
-            ByteBuffer dst = getNIOByteBuffer(dstIndex);
+            ByteBuffer dst = getNIOByteBuffer();
+            int pos = byteIndex(dstIndex);
 
             // Pick up attributes necessary to choose an efficient copy strategy
             int itemsize = getItemsize();
@@ -194,11 +194,11 @@
             // Strategy depends on whether items are laid end-to-end or there are gaps
             if (skip == 0) {
                 // Straight copy of contiguous bytes
+                dst.position(pos);
                 dst.put(src);
 
             } else if (itemsize == 1) {
                 // Non-contiguous copy: single byte items
-                int pos = dst.position();
                 for (int i = 0; i < count; i++) {
                     dst.position(pos);
                     dst.put(src.get());
@@ -208,7 +208,6 @@
 
             } else {
                 // Non-contiguous copy: each time, copy itemsize bytes at a time
-                int pos = dst.position();
                 for (int i = 0; i < count; i++) {
                     dst.position(pos);
                     // Delineate the next itemsize bytes in the src
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
@@ -104,7 +104,7 @@
      * and an item size of 1.
      */
     @Override
-    protected int byteIndex(int index) throws IndexOutOfBoundsException {
+    public int byteIndex(int index) throws IndexOutOfBoundsException {
         if (index < 0 || index >= shape[0]) {
             throw new IndexOutOfBoundsException();
         }
diff --git a/src/org/python/core/buffer/SimpleNIOBuffer.java b/src/org/python/core/buffer/SimpleNIOBuffer.java
--- a/src/org/python/core/buffer/SimpleNIOBuffer.java
+++ b/src/org/python/core/buffer/SimpleNIOBuffer.java
@@ -112,7 +112,7 @@
     }
 
     @Override
-    protected int byteIndex(int index) throws IndexOutOfBoundsException {
+    public final int byteIndex(int index) throws IndexOutOfBoundsException {
         return index0 + index;
     }
 
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
@@ -68,7 +68,7 @@
      * In <code>SimpleStringBuffer</code> we can simply return the argument.
      */
     @Override
-    protected final int byteIndex(int index) {
+    public final int byteIndex(int index) {
         // We do not check the index because String will do it for us.
         return index;
     }
@@ -97,8 +97,7 @@
     public PyBuffer getBufferSlice(int flags, int start, int count) {
         if (count > 0) {
             // The new string content is just a sub-string.
-            return new SimpleStringView(getRoot(), flags,
-                    bufString.substring(start, start + count));
+            return new SimpleStringView(getRoot(), flags, bufString.substring(start, start + count));
         } else {
             // Special case for count==0 where start out of bounds sometimes raises exception.
             return new ZeroByteBuffer.View(getRoot(), flags);
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
@@ -129,7 +129,7 @@
     }
 
     @Override
-    protected final int byteIndex(int index) throws IndexOutOfBoundsException {
+    public final int byteIndex(int index) throws IndexOutOfBoundsException {
         if (index < 0 || index >= shape[0]) {
             throw new IndexOutOfBoundsException();
         }
diff --git a/src/org/python/core/buffer/Strided1DNIOBuffer.java b/src/org/python/core/buffer/Strided1DNIOBuffer.java
--- a/src/org/python/core/buffer/Strided1DNIOBuffer.java
+++ b/src/org/python/core/buffer/Strided1DNIOBuffer.java
@@ -138,7 +138,7 @@
     }
 
     @Override
-    protected int byteIndex(int index) throws IndexOutOfBoundsException {
+    public final int byteIndex(int index) throws IndexOutOfBoundsException {
         return index0 + index * stride;
     }
 
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
@@ -47,7 +47,7 @@
      * In a ZeroByteBuffer, the index is always out of bounds.
      */
     @Override
-    protected int byteIndex(int index) throws IndexOutOfBoundsException {
+    public int byteIndex(int index) throws IndexOutOfBoundsException {
         // This causes all access to the bytes in to throw (since BaseBuffer calls it).
         throw new IndexOutOfBoundsException();
     }
@@ -56,7 +56,7 @@
      * In a ZeroByteBuffer, if the dimensions are right, the index is out of bounds anyway.
      */
     @Override
-    protected int byteIndex(int... indices) throws IndexOutOfBoundsException {
+    public int byteIndex(int... indices) throws IndexOutOfBoundsException {
         // Bootless dimension check takes precedence (for consistency with other buffers)
         checkDimension(indices);
         // This causes all access to the bytes to throw (since BaseBuffer calls it).
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
@@ -1061,59 +1061,6 @@
 
     }
 
-    /** Test method for {@link org.python.core.PyBuffer#getNIOByteBuffer(int)}. */
-    @Test
-    public void testGetNIOByteBufferInt() {
-        announce("getNIOByteBuffer (int)");
-
-        int n = ref.length, itemsize = view.getItemsize();
-        byte[] exp = new byte[itemsize], bytes = ref.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);
-            ByteBufferTestSupport.assertBytesEqual("getNIOByteBuffer(int) value", exp, bb);
-        }
-    }
-
-    /** Test method for {@link org.python.core.PyBuffer#getNIOByteBuffer(int[])}. */
-    @Test
-    public void testGetNIOByteBufferIntArray() {
-        int[] index = new int[1];
-        announce("getNIOByteBuffer (n-dim)");
-
-        int n = ref.length, itemsize = view.getItemsize();
-        byte[] exp = new byte[itemsize], bytes = ref.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);
-            ByteBufferTestSupport.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
-            // XXX ... but should it be TypeError here?
-            assertEquals(Py.BufferError, pye.type);
-        }
-    }
-
     /** Test method for {@link org.python.core.PyBuffer#hasArray()}. */
     @Test
     public void testHasArray() {

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list