[Jython-checkins] jython: Buffer api: eliminate BufferPointer as class
jeff.allen
jython-checkins at python.org
Sat Dec 1 00:30:03 CET 2012
http://hg.python.org/jython/rev/58f8ff3a6216
changeset: 6888:58f8ff3a6216
user: Jeff Allen <ja...py at farowl.co.uk>
date: Fri Nov 30 22:05:43 2012 +0000
summary:
Buffer api: eliminate BufferPointer as class
Improvements associated with the attempt to make java.nio.ByteBuffer part of the buffer API. The BufferPointer class is now PyBuffer.Pointer and only appears as a return type. Internals of BaseBuffer flattened and the implementation of Sliced1DBuffer made slicker with range-checking in the constructor. JUnit tests (tidied a bit) all pass.
files:
src/org/python/core/BufferPointer.java | 41 --
src/org/python/core/PyBuffer.java | 48 ++-
src/org/python/core/buffer/BaseBuffer.java | 157 +++------
src/org/python/core/buffer/SimpleBuffer.java | 123 +++++--
src/org/python/core/buffer/SimpleStringBuffer.java | 45 +-
src/org/python/core/buffer/SimpleWritableBuffer.java | 62 +--
src/org/python/core/buffer/Strided1DBuffer.java | 149 +++++---
src/org/python/core/buffer/Strided1DWritableBuffer.java | 43 +-
src/org/python/core/buffer/ZeroByteBuffer.java | 26 +-
src/org/python/core/io/TextIOBase.java | 4 +-
tests/java/org/python/core/PyBufferTest.java | 101 ++----
11 files changed, 399 insertions(+), 400 deletions(-)
diff --git a/src/org/python/core/BufferPointer.java b/src/org/python/core/BufferPointer.java
deleted file mode 100644
--- a/src/org/python/core/BufferPointer.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.python.core;
-
-/**
- * A class that references a specified <code>byte[]</code> array and an offset in it to be treated
- * as "index zero", for use in the buffer API. This class simply bundles together a reference to an
- * array and a particular offset within that array. It is used by the Jython buffer API roughly
- * where the CPython buffer API uses a C (char *) pointer.
- */
-public class BufferPointer {
-
- /**
- * Reference to the backing array. Usually this is the actual storage exported by a Python
- * object. In some contexts the consumer will be entitled to make changes to the contents of
- * this array, and in others, not. See {@link PyBuffer#isReadonly()}.
- */
- public final byte[] storage;
- /** Starting position within the array for index calculations: "index zero". */
- public final int offset;
-
- /**
- * Refer to an offset in the given array.
- *
- * @param storage array at reference
- * @param offset index of the first byte
- */
- public BufferPointer(byte[] storage, int offset) {
- // No checks: keep it simple
- this.storage = storage;
- this.offset = offset;
- }
-
- /**
- * Refer to the whole of a byte array.
- *
- * @param storage array at reference
- */
- public BufferPointer(byte[] storage) {
- this.storage = storage;
- this.offset = 0;
- }
-}
\ No newline at end of file
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
@@ -220,7 +220,7 @@
* In a simple buffer backed by a contiguous byte array, the result is a strided PyBuffer on the
* same storage but where the offset is adjusted by <i>s</i> and the stride is as supplied. If
* the current buffer is already strided and/or has an item size larger than single bytes, the
- * new offset, size and stride will be translated from the arguments given, through this
+ * new start index, length and stride will be translated from the arguments given, through this
* buffer's stride and item size. The consumer always expresses <code>start</code> and
* <code>strides</code> in terms of the abstract view of this buffer.
*
@@ -234,6 +234,33 @@
// Direct access to actual storage
//
+
+ /**
+ * A class that references a <code>byte[]</code> array and a particular offset within it, as the
+ * return type for methods that give direct access to byte-oriented data exported by a Python
+ * object. In some contexts the consumer will be entitled to make changes to the contents of
+ * 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.
+ */
+ public static class Pointer {
+
+ /** Reference to the array holding the bytes. */
+ public byte[] storage;
+ /** Starting position within the array for the data being pointed to. */
+ public int offset;
+
+ /**
+ * Construct a reference to the given array and offset.
+ *
+ * @param storage array at reference
+ * @param offset index of the reference byte
+ */
+ public Pointer(byte[] storage, int offset) {
+ this.storage = storage;
+ this.offset = offset;
+ }
+ }
+
/**
* Return a structure describing the slice of a byte array that holds the data being exported to
* the consumer. For a one-dimensional contiguous buffer, assuming the following client code
@@ -242,7 +269,7 @@
* <pre>
* PyBuffer a = obj.getBuffer();
* int itemsize = a.getItemsize();
- * BufferPointer b = a.getBuf();
+ * PyBuffer.Pointer b = a.getBuf();
* </pre>
*
* the item with index <code>k</code> is in the array <code>b.storage</code> at index
@@ -250,14 +277,14 @@
* inclusive. And if <code>itemsize==1</code>, the item is simply the byte
* <code>b.storage[b.offset + k]</code>
* <p>
- * If the buffer is multidimensional or non-contiguous, <code>b.storage[b.offset]</code> is
- * still the (first byte of) the item at index [0] or [0,...,0]. However, it is necessary to
- * navigate <code>b</code> using the <code>shape</code>, <code>strides</code> and maybe
+ * If the buffer is multidimensional or non-contiguous, <code>storage[offset]</code> is still
+ * the (first byte of) the item at index [0] or [0,...,0]. However, it is necessary to navigate
+ * <code>b.storage</code> using the <code>shape</code>, <code>strides</code> and maybe
* <code>suboffsets</code> provided by the API.
*
* @return structure defining the byte[] slice that is the shared data
*/
- BufferPointer getBuf();
+ PyBuffer.Pointer getBuf();
/**
* Return a structure describing the slice of a byte array that points to a single item from the
@@ -268,7 +295,7 @@
* int k = ... ;
* PyBuffer a = obj.getBuffer();
* int itemsize = a.getItemsize();
- * BufferPointer b = a.getPointer(k);
+ * PyBuffer.Pointer b = a.getPointer(k);
* </pre>
*
* the item with index <code>k</code> is in the array <code>b.storage</code> at index
@@ -282,7 +309,7 @@
* @param index in the buffer to position the pointer
* @return structure defining the byte[] slice that is the shared data
*/
- BufferPointer getPointer(int index);
+ PyBuffer.Pointer getPointer(int index);
/**
* Return a structure describing the slice of a byte array that points to a single item from the
@@ -295,7 +322,7 @@
* // ... calculation that assigns i, j, k
* PyBuffer a = obj.getBuffer();
* int itemsize = a.getItemsize();
- * BufferPointer b = a.getPointer(i,j,k);
+ * PyBuffer.Pointer b = a.getPointer(i,j,k);
* </pre>
*
* the item with index <code>[i,j,k]</code> is in the array <code>b.storage</code> at index
@@ -313,7 +340,7 @@
* @param indices multidimensional index at which to position the pointer
* @return structure defining the byte[] slice that is the shared data
*/
- BufferPointer getPointer(int... indices);
+ PyBuffer.Pointer getPointer(int... indices);
// Inherited from PyBUF and belonging here
//
@@ -345,4 +372,5 @@
*/
@Override
public String toString();
+
}
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
@@ -1,6 +1,5 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.BufferProtocol;
import org.python.core.Py;
import org.python.core.PyBUF;
@@ -47,6 +46,7 @@
* filled (difference from CPython). This value is returned by {@link #getShape()}.
*/
protected int[] shape;
+
/**
* Step sizes in the underlying buffer essential to correct translation of an index (or indices)
* into an index into the storage. The <code>strides</code> array should always be created and
@@ -54,11 +54,24 @@
* CPython). This value is returned by {@link #getStrides()}.
*/
protected int[] strides;
+
/**
- * Reference to a structure that wraps the underlying storage that the exporter is sharing with
- * the consumer.
+ * Reference to the underlying <code>byte[]</code> storage that the exporter is sharing with the
+ * consumer. The data need not occupy the whole array: in the constructor of a particular type
+ * of buffer, the exporter usually indicates an offset to the first significant byte and length
+ * (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)}).
*/
- protected BufferPointer buf;
+ protected byte[] storage;
+
+ /**
+ * Absolute index in <code>storage</code> of <code>item[0]</code>. In one dimension, for a
+ * positive <code>stride</code> this is equal to the offset of the first byte used in
+ * {@link #storage}, and for a negative <code>stride</code> it is the last. In an N-dimensional
+ * buffer with strides of mixed sign, it could be anywhere in the data.
+ */
+ protected int index0;
+
/**
* Count the number of times {@link #release()} must be called before actual release actions
* need to take place. Equivalently, this is the number of calls to
@@ -114,16 +127,10 @@
* exported, not the flags that form the consumer's request. The buffer will be read-only unless
* {@link PyBUF#WRITABLE} is set in the feature flags. {@link PyBUF#FORMAT} is implicitly added
* to the feature flags. The navigation arrays are all null, awaiting action by the sub-class
- * constructor. To complete initialisation, the sub-class normally must assign: {@link #buf},
- * {@link #shape}, and {@link #strides}, and call {@link #checkRequestFlags(int)} passing the
- * consumer's request flags.
- *
- * <pre>
- * this.buf = buf; // Wraps exported data
- * this.shape = shape; // Array of dimensions of exported data (in units)
- * this.strides = strides; // Byte offset between successive items in each dimension
- * checkRequestFlags(flags); // Check request is compatible with type
- * </pre>
+ * constructor. To complete initialisation, the sub-class normally must assign: the buffer (
+ * {@link #storage}, {@link #index0}), and the navigation arrays ({@link #shape},
+ * {@link #strides}), and call {@link #checkRequestFlags(int)} passing the consumer's request
+ * flags.
*
* @param featureFlags bit pattern that specifies the actual features allowed/required
*/
@@ -215,15 +222,21 @@
return shape;
}
+ /**
+ * {@inheritDoc}
+ * <p>
+ * The default implementation in <code>BaseBuffer</code> deals with the general one-dimensional
+ * case, with any item size and stride.
+ */
@Override
public int getLen() {
- // Correct if one-dimensional bytes. Override with itemsize*product(shape).
- return shape[0];
+ // Correct if one-dimensional. Override with itemsize*product(shape).
+ return shape[0] * getItemsize();
}
@Override
public byte byteAt(int index) throws IndexOutOfBoundsException {
- return buf.storage[calcIndex(index)];
+ return storage[calcIndex(index)];
}
@Override
@@ -236,7 +249,7 @@
if (isReadonly()) {
throw notWritable();
}
- buf.storage[calcIndex(index)] = value;
+ storage[calcIndex(index)] = value;
}
/**
@@ -248,12 +261,12 @@
*/
protected int calcIndex(int index) throws IndexOutOfBoundsException {
// Treat as one-dimensional
- return buf.offset + index * getStrides()[0];
+ return index0 + index * getStrides()[0];
}
@Override
public byte byteAt(int... indices) throws IndexOutOfBoundsException {
- return buf.storage[calcIndex(indices)];
+ return storage[calcIndex(indices)];
}
@Override
@@ -266,29 +279,29 @@
if (isReadonly()) {
throw notWritable();
}
- buf.storage[calcIndex(indices)] = value;
+ storage[calcIndex(indices)] = value;
}
/**
* 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 as seen by the buffer.
+ * 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 the <code>BufferPointer</code> member
- * {@link #buf}, the buffer implementation may use <code>buf.storage[calcIndex(i)]</code> to
- * reference the (first byte of) the item x[i]. This is what the default implementation of
- * accessors in <code>BaseBuffer</code> will do. In the simplest cases, this is fairly
- * inefficient, and an implementation will override the accessors to in-line the calculation.
- * The default implementation here is suited to N-dimensional arrays.
+ * In the usual case where the storage is referenced via the {@link #storage} and
+ * {@link #index0} members, the buffer implementation may use <code>storage[calcIndex(i)]</code>
+ * to reference the (first byte of) the item x[i]. This is what the default implementation of
+ * accessors in <code>BaseBuffer</code> will do. In the simplest cases, calling
+ * <code>calcIndex</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 of the item from the consumer
* @return index relative to item x[0,...,0] in actual storage
*/
protected int calcIndex(int... indices) throws IndexOutOfBoundsException {
final int N = checkDimension(indices);
- // In general: buf.offset + sum(k=0,N-1) indices[k]*strides[k]
- int index = buf.offset;
+ // In general: index0 + sum(k=0,N-1) indices[k]*strides[k]
+ int index = index0;
if (N > 0) {
int[] strides = getStrides();
for (int k = 0; k < N; k++) {
@@ -332,13 +345,13 @@
// Strategy depends on whether items are laid end-to-end contiguously or there are gaps
if (skip == 0) {
// stride == itemsize: straight copy of contiguous bytes
- System.arraycopy(buf.storage, s, dest, d, length * itemsize);
+ System.arraycopy(storage, s, dest, d, length * itemsize);
} else if (itemsize == 1) {
// Non-contiguous copy: single byte items
int limit = s + length * stride;
for (; s < limit; s += stride) {
- dest[d++] = buf.storage[s];
+ dest[d++] = storage[s];
}
} else {
@@ -347,7 +360,7 @@
for (; s < limit; s += skip) {
int t = s + itemsize;
while (s < t) {
- dest[d++] = buf.storage[s++];
+ dest[d++] = storage[s++];
}
}
}
@@ -381,13 +394,13 @@
// Strategy depends on whether items are laid end-to-end or there are gaps
if (skip == 0) {
// Straight copy of contiguous bytes
- System.arraycopy(src, srcPos, buf.storage, d, length * itemsize);
+ System.arraycopy(src, srcPos, storage, d, length * itemsize);
} else if (itemsize == 1) {
// Non-contiguous copy: single byte items
int limit = d + length * stride;
for (; d != limit; d += stride) {
- buf.storage[d] = src[s++];
+ storage[d] = src[s++];
}
} else {
@@ -396,7 +409,7 @@
for (; d != limit; d += skip) {
int t = d + itemsize;
while (d < t) {
- buf.storage[d++] = src[s++];
+ storage[d++] = src[s++];
}
}
}
@@ -430,21 +443,21 @@
// Strategy depends on whether items are laid end-to-end or there are gaps
if (stride == itemsize) {
// Straight copy to contiguous bytes
- src.copyTo(buf.storage, d);
+ src.copyTo(storage, d);
} else if (itemsize == 1) {
// Non-contiguous copy: single byte items
int limit = d + src.getLen() * stride;
for (; d != limit; d += stride) {
- buf.storage[d] = src.byteAt(s++);
+ storage[d] = src.byteAt(s++);
}
} else {
// Non-contiguous copy: each time, copy itemsize bytes then skip
int limit = d + src.getShape()[0] * stride;
for (; d != limit; d += stride) {
- BufferPointer srcItem = src.getPointer(s++);
- System.arraycopy(srcItem.storage, srcItem.offset, buf.storage, d, itemsize);
+ Pointer srcItem = src.getPointer(s++);
+ System.arraycopy(srcItem.storage, srcItem.offset, storage, d, itemsize);
}
}
@@ -521,18 +534,18 @@
// @Override public PyBuffer getBufferSlice(int flags, int start, int length, int stride) {}
@Override
- public BufferPointer getBuf() {
- return buf;
+ public Pointer getBuf() {
+ return new Pointer(storage, index0);
}
@Override
- public BufferPointer getPointer(int index) {
- return new BufferPointer(buf.storage, calcIndex(index));
+ public Pointer getPointer(int index) throws IndexOutOfBoundsException {
+ return new Pointer(storage, calcIndex(index));
}
@Override
- public BufferPointer getPointer(int... indices) {
- return new BufferPointer(buf.storage, calcIndex(indices));
+ public Pointer getPointer(int... indices) throws IndexOutOfBoundsException {
+ return new Pointer(storage, calcIndex(indices));
}
@Override
@@ -620,58 +633,6 @@
}
/**
- * Convenience method for checking arguments to slice formation, that the start and end elements
- * are within the buffer. An exception is raised if <code>start<0</code> or
- * <code>start+length>shape[0]</code>. This logic is correct for one-dimensional arrays (of
- * any item size) and stride. In the context we use this, <code>length</code> is guaranteed by
- * the acller to be non-negative.
- *
- * @param start index to check
- * @param length number of elements in slice (must be >0)
- * @throws IndexOutOfBoundsException
- */
- protected void checkSlice(int start, int length) throws IndexOutOfBoundsException {
- // Between the last element of the slice and the end of the buffer there are ...
- int margin = shape[0] - start - length;
- if ((start | margin) < 0) {
- throw new IndexOutOfBoundsException("invalid slice of buffer");
- }
- }
-
- /**
- * Convenience method for checking arguments to slice formation, that the start and end elements
- * are within the buffer. An exception is raised if either of <code>start</code> or
- * <code>end=start+(length-1)*stride+1</code> is <code><0</code> or <code>>shape[0]</code>
- * . This logic is correct for one-dimensional arrays (of any item size) and current stride.
- * Note that the parameter stride is in terms of this biuffer's indexing. In the context we use
- * this, <code>length</code> is guaranteed by the acller to be non-negative.
- *
- * @param start index to check
- * @param length number of elements in slice (must be >0)
- * @param stride in buffer elements between items of the slice
- * @throws IndexOutOfBoundsException
- */
- protected void checkSlice(int start, int length, int stride) throws IndexOutOfBoundsException {
- /*
- * Simpler to check if we know which is the smaller of the two ends, which depends on the
- * sign of the stride.
- */
- int lo, hi;
- if (stride > 0) {
- lo = start;
- hi = start + (length - 1) * stride + 1;
- } else {
- hi = start;
- lo = start + (length - 1) * stride + 1;
- }
- // Between the last element of the slice and the end of the buffer there are ...
- int margin = shape[0] - hi;
- if ((lo | margin) < 0) {
- throw new IndexOutOfBoundsException("invalid slice of buffer");
- }
- }
-
- /**
* The toString() method of a buffer reproduces the values in the buffer (as unsigned integers)
* as the character codes of a <code>String</code>.
*/
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
@@ -1,6 +1,5 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.PyBuffer;
import org.python.core.PyException;
import org.python.core.util.StringUtil;
@@ -17,16 +16,10 @@
/**
* Provide an instance of <code>SimpleBuffer</code> with navigation variables partly
- * initialised, for sub-class use. One-dimensional arrays without slicing are C- and
- * F-contiguous. To complete initialisation, the sub-class normally must assign: {@link #buf}
- * and {@link #shape}[0], and call {@link #checkRequestFlags(int)} passing the consumer's
- * request flags.
- *
- * <pre>
- * this.buf = buf; // Wraps exported data
- * this.shape[0] = n; // Number of units in exported data
- * checkRequestFlags(flags); // Check request is compatible with type
- * </pre>
+ * initialised, for sub-class use. One-dimensional arrays without strides are C- and
+ * F-contiguous. To complete initialisation, the sub-class must normally assign the buffer (
+ * {@link #storage}, {@link #index0}), and the navigation ({@link #shape} array), and then call
+ * {@link #checkRequestFlags(int)} passing the consumer's request flags.
*/
protected SimpleBuffer() {
super(CONTIGUITY | SIMPLE);
@@ -37,22 +30,70 @@
}
/**
+ * Provide an instance of <code>SimpleBuffer</code> with navigation variables initialised, for
+ * sub-class use. The buffer ({@link #storage}, {@link #index0}), and the {@link #shape} array
+ * will be initialised from the arguments (which are checked for range). The {@link #strides} is
+ * set for (one-byte) unit stride. Only the call to {@link #checkRequestFlags(int)}, passing the
+ * consumer's request flags really remains for the sub-class constructor to do.
+ *
+ * <pre>
+ * super(storage, index0, size);
+ * checkRequestFlags(flags); // Check request is compatible with type
+ * </pre>
+ *
+ * @param storage the array of bytes storing the implementation of the exporting object
+ * @param index0 offset where the data starts in that array (item[0])
+ * @param size the number of bytes occupied
+ * @throws NullPointerException if <code>storage</code> is null
+ * @throws ArrayIndexOutOfBoundsException if <code>index0</code> and <code>size</code> are
+ * inconsistent with <code>storage.length</code>
+ */
+ public SimpleBuffer(byte[] storage, int index0, int size) throws PyException,
+ ArrayIndexOutOfBoundsException {
+ this();
+ this.storage = storage; // Exported data
+ this.index0 = index0; // Index to be treated as item[0]
+ this.shape[0] = size; // Number of items in exported data
+
+ // Check arguments using the "all non-negative" trick
+ if ((index0 | size | storage.length - (index0 + size)) < 0) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ /**
* Provide an instance of <code>SimpleBuffer</code>, on a slice of a byte array, meeting the
* consumer's expectations as expressed in the <code>flags</code> argument, which is checked
* against the capabilities of the buffer type.
*
* @param flags consumer requirements
* @param storage the array of bytes storing the implementation of the exporting object
- * @param offset where the data starts in that array (item[0])
+ * @param index0 offset where the data starts in that array (item[0])
* @param size the number of bytes occupied
+ * @throws NullPointerException if <code>storage</code> is null
+ * @throws ArrayIndexOutOfBoundsException if <code>index0</code> and <code>size</code> are
+ * inconsistent with <code>storage.length</code>
* @throws PyException (BufferError) when expectations do not correspond with the type
*/
- public SimpleBuffer(int flags, byte[] storage, int offset, int size) throws PyException {
+ public SimpleBuffer(int flags, byte[] storage, int index0, int size) throws PyException,
+ ArrayIndexOutOfBoundsException, NullPointerException {
+ this(storage, index0, size); // Construct checked SimpleBuffer
+ checkRequestFlags(flags); // Check request is compatible with type
+ }
+
+ /**
+ * Provide an instance of <code>SimpleBuffer</code>, on the entirety of a byte array, with
+ * navigation variables initialised, for sub-class use. The buffer ( {@link #storage},
+ * {@link #index0}), and the navigation ({@link #shape} array) will be initialised from the
+ * array argument.
+ *
+ * @param storage the array of bytes storing the implementation of the exporting object
+ * @throws NullPointerException if <code>storage</code> is null
+ */
+ public SimpleBuffer(byte[] storage) throws NullPointerException {
this();
- // Wrap the exported data on a BufferPointer object
- this.buf = new BufferPointer(storage, offset);
- this.shape[0] = size; // Number of units in exported data
- checkRequestFlags(flags); // Check request is compatible with type
+ this.storage = storage; // Exported data (index0=0 from initialisation)
+ this.shape[0] = storage.length; // Number of units in whole array
}
/**
@@ -62,10 +103,12 @@
*
* @param flags consumer requirements
* @param storage the array of bytes storing the implementation of the exporting object
+ * @throws NullPointerException if <code>storage</code> is null
* @throws PyException (BufferError) when expectations do not correspond with the type
*/
- public SimpleBuffer(int flags, byte[] storage) throws PyException {
- this(flags, storage, 0, storage.length);
+ public SimpleBuffer(int flags, byte[] storage) throws PyException, NullPointerException {
+ this(storage); // Construct SimpleBuffer on whole array
+ checkRequestFlags(flags); // Check request is compatible with type
}
@Override
@@ -80,9 +123,21 @@
* one-dimension.
*/
@Override
+ public int getLen() {
+ // Simplify for one-dimensional contiguous bytes
+ return shape[0];
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * <code>SimpleBuffer</code> provides an implementation optimised for contiguous bytes in
+ * one-dimension.
+ */
+ @Override
public byte byteAt(int index) throws IndexOutOfBoundsException {
// Implement directly: a bit quicker than the default
- return buf.storage[buf.offset + index];
+ return storage[index0 + index];
}
/**
@@ -94,12 +149,12 @@
@Override
public int intAt(int index) throws IndexOutOfBoundsException {
// Implement directly: a bit quicker than the default
- return 0xff & buf.storage[buf.offset + index];
+ return 0xff & storage[index0 + index];
}
@Override
protected int calcIndex(int index) throws IndexOutOfBoundsException {
- return buf.offset + index;
+ return index0 + index;
}
/**
@@ -130,18 +185,16 @@
@Override
public void copyTo(int srcIndex, byte[] dest, int destPos, int length)
throws IndexOutOfBoundsException {
- System.arraycopy(buf.storage, buf.offset + srcIndex, dest, destPos, length);
+ System.arraycopy(storage, index0 + srcIndex, dest, destPos, length);
}
@Override
public PyBuffer getBufferSlice(int flags, int start, int length) {
if (length > 0) {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length);
// Translate relative to underlying buffer
- int compIndex0 = buf.offset + start;
+ int compIndex0 = index0 + start;
// Create the slice from the sub-range of the buffer
- return new SimpleView(getRoot(), flags, buf.storage, compIndex0, length);
+ return new SimpleView(getRoot(), flags, storage, compIndex0, length);
} else {
// Special case for length==0 where above logic would fail. Efficient too.
return new ZeroByteBuffer.View(getRoot(), flags);
@@ -165,23 +218,21 @@
return getBufferSlice(flags, start, length);
} else {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length, stride);
// Translate relative to underlying buffer
- int compIndex0 = buf.offset + start;
+ int compIndex0 = index0 + start;
// Construct a view, taking a lock on the root object (this or this.root)
- return new Strided1DBuffer.SlicedView(getRoot(), flags, buf.storage, compIndex0,
- length, stride);
+ return new Strided1DBuffer.SlicedView(getRoot(), flags, storage, compIndex0, length,
+ stride);
}
}
@Override
- public BufferPointer getPointer(int index) {
- return new BufferPointer(buf.storage, buf.offset + index);
+ public Pointer getPointer(int index) throws IndexOutOfBoundsException {
+ return new Pointer(storage, index0 + index);
}
@Override
- public BufferPointer getPointer(int... indices) {
+ public Pointer getPointer(int... indices) throws IndexOutOfBoundsException {
checkDimension(indices.length);
return getPointer(indices[0]);
}
@@ -189,7 +240,7 @@
@Override
public String toString() {
// For contiguous bytes in one dimension we can avoid the intAt() calls
- return StringUtil.fromBytes(buf.storage, buf.offset, shape[0]);
+ return StringUtil.fromBytes(storage, index0, shape[0]);
}
/**
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
@@ -1,13 +1,12 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.PyBuffer;
import org.python.core.util.StringUtil;
/**
* 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 {@link BufferPointer} result), and therefore
+ * the data as a byte array (those parts that involve a {@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
@@ -29,7 +28,6 @@
* @param flags consumer requirements
*/
public SimpleStringBuffer(int flags, String bufString) {
- super();
// Save the backing string
this.bufString = bufString;
shape[0] = bufString.length();
@@ -101,6 +99,7 @@
* <p>
* The <code>SimpleStringBuffer</code> implementation creates an actual byte buffer.
*/
+ @Override
public PyBuffer getBufferSlice(int flags, int start, int length, int stride) {
if (stride == 1) {
// Unstrided slice of simple buffer is itself simple
@@ -113,43 +112,51 @@
}
/**
- * {@inheritDoc}
- * <p>
- * This method creates an actual byte buffer from the String if none yet exists.
+ * This method creates an actual byte array from the underlying String if none yet exists.
*/
- @Override
- public BufferPointer getBuf() {
- if (buf == null) {
- // We can't avoid creating buf any longer
- buf = new BufferPointer(StringUtil.toBytes(bufString));
+ private void ensureHaveBytes() {
+ if (storage == null) {
+ // We can't avoid creating the byte array any longer (index0 already correct)
+ storage = StringUtil.toBytes(bufString);
}
- return buf;
}
/**
* {@inheritDoc}
* <p>
- * This method creates an actual byte buffer from the String if none yet exists.
+ * This method creates an actual byte array from the underlying String if none yet exists.
*/
@Override
- public BufferPointer getPointer(int index) {
- getBuf(); // Ensure buffer created
+ public Pointer getBuf() {
+ ensureHaveBytes();
+ return super.getBuf();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This method creates an actual byte array from the underlying String if none yet exists.
+ */
+ @Override
+ public Pointer getPointer(int index) {
+ ensureHaveBytes();
return super.getPointer(index);
}
/**
* {@inheritDoc}
* <p>
- * This method creates an actual byte buffer from the String if none yet exists.
+ * This method creates an actual byte array from the underlying String if none yet exists.
*/
@Override
- public BufferPointer getPointer(int... indices) {
- getBuf(); // Ensure buffer created
+ public Pointer getPointer(int... indices) {
+ ensureHaveBytes();
return super.getPointer(indices);
}
/**
- * The <code>toString()</code> method of a <code>SimpleStringBuffer</code> simply produces the underlying <code>String</code>.
+ * The <code>toString()</code> method of a <code>SimpleStringBuffer</code> simply produces the
+ * underlying <code>String</code>.
*/
@Override
public String toString() {
diff --git a/src/org/python/core/buffer/SimpleWritableBuffer.java b/src/org/python/core/buffer/SimpleWritableBuffer.java
--- a/src/org/python/core/buffer/SimpleWritableBuffer.java
+++ b/src/org/python/core/buffer/SimpleWritableBuffer.java
@@ -1,6 +1,5 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.PyBuffer;
import org.python.core.PyException;
@@ -10,35 +9,36 @@
public class SimpleWritableBuffer extends SimpleBuffer {
/**
- * Provide an instance of <code>SimpleWritableBuffer</code>, on a slice of a byte array, meeting the consumer's expectations
- * as expressed in the <code>flags</code> argument, which is checked against the capabilities of
- * the buffer type.
+ * Provide an instance of <code>SimpleWritableBuffer</code>, on a slice of a byte array, meeting
+ * the consumer's expectations as expressed in the <code>flags</code> argument, which is checked
+ * against the capabilities of the buffer type.
*
* @param flags consumer requirements
* @param storage the array of bytes storing the implementation of the exporting object
- * @param offset where the data starts in that array (item[0])
+ * @param index0 offset where the data starts in that array (item[0])
* @param size the number of bytes occupied
* @throws PyException (BufferError) when expectations do not correspond with the type
*/
- public SimpleWritableBuffer(int flags, byte[] storage, int offset, int size) throws PyException {
+ public SimpleWritableBuffer(int flags, byte[] storage, int index0, int size)
+ throws PyException, NullPointerException {
+ super(storage, index0, size); // Construct checked SimpleBuffer
addFeatureFlags(WRITABLE);
- // Wrap the exported data on a BufferPointer object
- this.buf = new BufferPointer(storage, offset);
- this.shape[0] = size; // Number of units in exported data
- checkRequestFlags(flags); // Check request is compatible with type
+ checkRequestFlags(flags); // Check request is compatible with type
}
/**
- * Provide an instance of <code>SimpleWritableBuffer</code>, on the entirety of a byte array, meeting the consumer's expectations
- * as expressed in the <code>flags</code> argument, which is checked against the capabilities of
- * the buffer type.
+ * Provide an instance of <code>SimpleWritableBuffer</code>, on the entirety of a byte array,
+ * meeting the consumer's expectations as expressed in the <code>flags</code> argument, which is
+ * checked against the capabilities of the buffer type.
*
* @param flags consumer requirements
* @param storage the array of bytes storing the implementation of the exporting object
* @throws PyException (BufferError) when expectations do not correspond with the type
*/
- public SimpleWritableBuffer(int flags, byte[] storage) throws PyException {
- this(flags, storage, 0, storage.length);
+ public SimpleWritableBuffer(int flags, byte[] storage) throws PyException, NullPointerException {
+ super(storage); // Construct SimpleBuffer on whole array
+ addFeatureFlags(WRITABLE);
+ checkRequestFlags(flags); // Check request is compatible with type
}
@Override
@@ -55,7 +55,7 @@
@Override
public void storeAt(byte value, int index) {
// Implement directly and don't ask whether read-only
- buf.storage[buf.offset + index] = value;
+ storage[index0 + index] = value;
}
/**
@@ -78,7 +78,7 @@
*/
@Override
public void copyFrom(byte[] src, int srcPos, int destIndex, int length) {
- System.arraycopy(src, srcPos, buf.storage, buf.offset + destIndex, length);
+ System.arraycopy(src, srcPos, storage, index0 + destIndex, length);
}
/**
@@ -89,13 +89,11 @@
*/
@Override
public void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException {
-
if (src.getLen() != getLen()) {
throw differentStructure();
}
-
// Get the source to deliver efficiently to our byte storage
- src.copyTo(buf.storage, buf.offset);
+ src.copyTo(storage, index0);
}
/**
@@ -107,12 +105,10 @@
@Override
public PyBuffer getBufferSlice(int flags, int start, int length) {
if (length > 0) {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length);
// Translate relative to underlying buffer
- int compIndex0 = buf.offset + start;
+ int compIndex0 = index0 + start;
// Create the slice from the sub-range of the buffer
- return new SimpleView(getRoot(), flags, buf.storage, compIndex0, length);
+ return new SimpleView(getRoot(), flags, storage, compIndex0, length);
} else {
// Special case for length==0 where above logic would fail. Efficient too.
return new ZeroByteBuffer.View(getRoot(), flags);
@@ -133,19 +129,17 @@
return getBufferSlice(flags, start, length);
} else {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length, stride);
// Translate relative to underlying buffer
- int compIndex0 = buf.offset + start;
+ int compIndex0 = index0 + start;
// Construct a view, taking a lock on the root object (this or this.root)
- return new Strided1DWritableBuffer.SlicedView(getRoot(), flags, buf.storage,
- compIndex0, length, stride);
+ return new Strided1DWritableBuffer.SlicedView(getRoot(), flags, storage, compIndex0,
+ length, stride);
}
}
/**
- * A <code>SimpleWritableBuffer.SimpleView</code> represents a contiguous subsequence of
- * another <code>SimpleWritableBuffer</code>.
+ * A <code>SimpleWritableBuffer.SimpleView</code> represents a contiguous subsequence of another
+ * <code>SimpleWritableBuffer</code>.
*/
static class SimpleView extends SimpleWritableBuffer {
@@ -158,12 +152,12 @@
* @param root buffer which will be acquired and must be released ultimately
* @param flags the request flags of the consumer that requested the slice
* @param storage the array of bytes storing the implementation of the exporting object
- * @param offset where the data starts in that array (item[0])
+ * @param index0 offset where the data starts in that array (item[0])
* @param size the number of bytes occupied
*/
- public SimpleView(PyBuffer root, int flags, byte[] storage, int offset, int size) {
+ public SimpleView(PyBuffer root, int flags, byte[] storage, int index0, int size) {
// Create a new SimpleBuffer on the buffer passed in (part of the root)
- super(flags, storage, offset, size);
+ super(flags, storage, index0, size);
// Get a lease on the root PyBuffer
this.root = root.getBuffer(FULL_RO);
}
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
@@ -1,26 +1,25 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.PyBuffer;
import org.python.core.PyException;
/**
* Read-only buffer API over a one-dimensional array of one-byte items, that are evenly-spaced in a
- * storage array. The buffer has a <code>buf</code> property in the usual way, designating a slice
- * (or all) of a byte array, but also a <code>stride</code> property (equal to
- * <code>getStrides()[0]</code>).
+ * storage array. The buffer has <code>storage</code>, <code>index0</code> and <code>length</code>
+ * properties in the usual way, designating a slice (or all) of a byte array, but also a
+ * <code>stride</code> property (equal to <code>getStrides()[0]</code>).
* <p>
- * Let this underlying buffer be the byte array <i>u(i)</i> for <i>i=a..a+N</i>, let <i>x</i> be the
+ * Let this underlying buffer be the byte array <i>u(i)</i> for <i>i=0..N-1</i>, let <i>x</i> be the
* <code>Strided1DBuffer</code>, and let the stride be <i>p</i>. The storage works as follows.
* Designate by <i>x(j)</i>, for <i>j=0..L-1</i>, the byte at index <i>j</i>, that is, the byte
- * retrieved by <code>x.byteAt(j)</code>. Then,
- * <ul>
- * <li>when <i>p>0</i>, we store <i>x(j)</i> at <i>u(a+pj)</i>, that is, <i>x(0)</i> is at
- * <i>u(a)</i> and the byte array slice size should be <i>N = (L-1)p+1</i>.</li>
- * <li>when <i>p<0</i>, we store <i>x(j)</i> at <i>u((a+N-1)+pj)</i>, that is, <i>x(0)</i> is at
- * <i>u(a+N-1)</i>, and the byte array slice size should be <i>N = (L-1)(-p)+1</i>.</li>
- * <li><i>p=0</i> is not a useful stride.</li>
- * </ul>
+ * retrieved by <code>x.byteAt(j)</code>. Then, we store <i>x(j)</i> at <i>u(a+pj)</i>, that is,
+ * <i>x(0)</i> is at <i>u(a)</i>. When we construct such a buffer, we have to supply <i>a</i> =
+ * <code>index0</code>, <i>L</i> = <code>length</code>, and <i>p</i> = <code>stride</code> as the
+ * constructor arguments. The last item in the slice <i>x(L-1)</i> is stored at <i>u(a+p(L-1))</i>.
+ * If <i>p<0</i> and <i>L>1</i>, this will be to the left of <i>u(a)</i>, so the constructor
+ * argument index0 is not then the low index of the range occupied by the data. Clearly both these
+ * indexes must be in the range 0 to <i>N-1</i> inclusive, a rule enforced by the constructors
+ * (unless <i>L=0</i>, when it is assumed no array access will take place).
* <p>
* The class may be used by exporters to create a strided slice (e.g. to export the diagonal of a
* matrix) and in particular by other buffers to create strided slices of themselves, such as to
@@ -38,13 +37,14 @@
/**
* Provide an instance of <code>Strided1DBuffer</code> with navigation variables partly
* initialised, for sub-class use. To complete initialisation, the sub-class normally must
- * assign: {@link #buf}, {@link #shape}[0], and {@link #stride}, and call
- * {@link #checkRequestFlags(int)} passing the consumer's request flags.
+ * assign the navigational properties and call {@link #checkRequestFlags(int)} passing the
+ * consumer's request flags.
*
* <pre>
- * this.buf = buf; // Wraps exported data
- * setStride(stride); // Stride, shape[0] and index0 all set consistently
- * checkRequestFlags(flags); // Check request is compatible with type
+ * this.storage = storage; // Exported data
+ * this.index0 = index0; // Index to be treated as item[0]
+ * this.shape[0] = length; // Number of items in exported data
+ * this.stride = stride; // Between items
* </pre>
*
* The pre-defined {@link #strides} field remains <code>null</code> until {@link #getStrides} is
@@ -59,38 +59,87 @@
}
/**
+ * Provide an instance of <code>Strided1DBuffer</code> with navigation variables initialised,
+ * for sub-class use. The buffer ( {@link #storage}, {@link #index0}), and the navigation (
+ * {@link #shape} array and {@link #stride}) will be initialised from the arguments (which are
+ * checked for range).
+ *
+ * @param storage raw byte array containing exported data
+ * @param index0 index into storage of item[0]
+ * @param length number of items in the slice
+ * @param stride in between successive elements of the new PyBuffer
+ * @throws NullPointerException if <code>storage</code> is null
+ * @throws ArrayIndexOutOfBoundsException if <code>index0</code>, <code>length</code> and
+ * <code>stride</code> are inconsistent with <code>storage.length</code>
+ */
+ public Strided1DBuffer(byte[] storage, int index0, int length, int stride)
+ throws ArrayIndexOutOfBoundsException, NullPointerException {
+ this();
+ this.storage = storage; // Exported data
+ this.index0 = index0; // Index to be treated as item[0]
+ this.shape[0] = length; // Number of items in exported data
+ this.stride = stride; // Between items
+
+ if (length == 0) {
+ // Nothing to check as we'll make no accesses
+ addFeatureFlags(CONTIGUITY);
+
+ } else {
+ // Need to check lowest and highest index against array
+ int lo, hi;
+
+ if (stride == 1) {
+ lo = index0; // First byte of item[0]
+ hi = index0 + length; // Last byte of item[L-1] + 1
+ addFeatureFlags(CONTIGUITY);
+
+ } else if (stride > 1) {
+ lo = index0; // First byte of item[0]
+ hi = index0 + (length - 1) * stride + 1; // Last byte of item[L-1] + 1
+
+ } else {
+ hi = index0 + 1; // Last byte of item[0] + 1
+ lo = index0 + (length - 1) * stride; // First byte of item[L-1]
+ }
+
+ // Check indices using "all non-negative" trick
+ if ((length | lo | (storage.length - lo) | hi | (storage.length - hi)) < 0) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+ }
+
+ /**
* Provide an instance of <code>Strided1DBuffer</code> on a particular array of bytes specifying
* a starting index, the number of items in the result, and a byte-indexing stride. The result
* of <code>byteAt(i)</code> will be equal to <code>storage[index0+stride*i]</code> (whatever
- * the sign of <code>stride>0</code>), valid for <code>0<=i<length</code>.
+ * the sign of <code>stride</code>), valid for <code>0<=i<length</code>. The constructor
+ * checks that all these indices lie within the <code>storage</code> array (unless
+ * <code>length=0</code>).
* <p>
* The constructed <code>PyBuffer</code> meets the consumer's expectations as expressed in the
* <code>flags</code> argument, or an exception will be thrown if these are incompatible with
* the type (e.g. the consumer does not specify that it understands the strides array). Note
* that the actual range in the <code>storage</code> array, the lowest and highest index, is not
* explicitly passed, but is implicit in <code>index0</code>, <code>length</code> and
- * <code>stride</code>. The caller is responsible for checking these fall within the array, or
- * the sub-range the caller is allowed to use.
+ * <code>stride</code>. The constructor checks that these indices lie within the
+ * <code>storage</code> array (unless <code>length=0</code>).
*
* @param flags consumer requirements
* @param storage raw byte array containing exported data
* @param index0 index into storage of item[0]
* @param length number of items in the slice
* @param stride in between successive elements of the new PyBuffer
+ * @throws NullPointerException if <code>storage</code> is null
+ * @throws ArrayIndexOutOfBoundsException if <code>index0</code>, <code>length</code> and
+ * <code>stride</code> are inconsistent with <code>storage.length</code>
* @throws PyException (BufferError) when expectations do not correspond with the type
*/
public Strided1DBuffer(int flags, byte[] storage, int index0, int length, int stride)
- throws PyException {
- // Arguments programme the object directly
- this();
- this.shape[0] = length;
- this.buf = new BufferPointer(storage, index0);
- this.stride = stride;
- if (stride == 1) {
- // Really this is a simple buffer
- addFeatureFlags(CONTIGUITY);
- }
+ throws ArrayIndexOutOfBoundsException, NullPointerException, PyException {
+ this(storage, index0, length, stride);
checkRequestFlags(flags); // Check request is compatible with type
+
}
@Override
@@ -100,12 +149,12 @@
@Override
public byte byteAt(int index) throws IndexOutOfBoundsException {
- return buf.storage[buf.offset + index * stride];
+ return storage[index0 + index * stride];
}
@Override
protected int calcIndex(int index) throws IndexOutOfBoundsException {
- return buf.offset + index * stride;
+ return index0 + index * stride;
}
@Override
@@ -123,19 +172,19 @@
public void copyTo(int srcIndex, byte[] dest, int destPos, int length)
throws IndexOutOfBoundsException {
// Data is here in the buffers
- int s = buf.offset + srcIndex * stride;
+ int s = index0 + srcIndex * stride;
int d = destPos;
// Strategy depends on whether items are laid end-to-end contiguously or there are gaps
if (stride == 1) {
// stride == itemsize: straight copy of contiguous bytes
- System.arraycopy(buf.storage, s, dest, d, length);
+ System.arraycopy(storage, s, dest, d, length);
} else {
// Non-contiguous copy: single byte items
int limit = s + length * stride;
for (; s != limit; s += stride) {
- dest[d++] = buf.storage[s];
+ dest[d++] = storage[s];
}
}
}
@@ -147,30 +196,18 @@
* one dimension. In that case, <i>x(i) = u(r+ip)</i> for <i>i = 0..L-1</i> where u is the
* underlying buffer, and <i>r</i>, <i>p</i> and <i>L</i> are the start, stride and length with
* which <i>x</i> was created from <i>u</i>. Thus <i>y(k) = u(r+sp+kmp)</i>, that is, the
- * composite offset is <i>r+sp</i> and the composite stride is <i>mp</i>.
+ * composite <code>index0</code> is <i>r+sp</i> and the composite <code>stride</code> is
+ * <i>mp</i>.
*/
@Override
public PyBuffer getBufferSlice(int flags, int start, int length, int stride) {
if (length > 0) {
- int compStride;
-
- if (stride == 1) {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length);
- // Composite stride is same as original stride
- compStride = this.stride;
- } else {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length, stride);
- // Composite stride is product
- compStride = this.stride * stride;
- }
-
// Translate start relative to underlying buffer
- int compIndex0 = buf.offset + start * this.stride;
+ int compStride = this.stride * stride;
+ int compIndex0 = index0 + start * this.stride;
// Construct a view, taking a lock on the root object (this or this.root)
- return new SlicedView(getRoot(), flags, buf.storage, compIndex0, length, compStride);
+ return new SlicedView(getRoot(), flags, storage, compIndex0, length, compStride);
} else {
// Special case for length==0 where above logic would fail. Efficient too.
@@ -179,12 +216,12 @@
}
@Override
- public BufferPointer getPointer(int index) {
- return new BufferPointer(buf.storage, buf.offset + index * stride);
+ public Pointer getPointer(int index) {
+ return new Pointer(storage, index0 + index * stride);
}
@Override
- public BufferPointer getPointer(int... indices) {
+ public Pointer getPointer(int... indices) {
// BaseBuffer implementation can be simplified since if indices.length!=1 we error.
checkDimension(indices.length);
return getPointer(indices[0]);
diff --git a/src/org/python/core/buffer/Strided1DWritableBuffer.java b/src/org/python/core/buffer/Strided1DWritableBuffer.java
--- a/src/org/python/core/buffer/Strided1DWritableBuffer.java
+++ b/src/org/python/core/buffer/Strided1DWritableBuffer.java
@@ -1,6 +1,5 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.PyBuffer;
import org.python.core.PyException;
@@ -30,19 +29,14 @@
* @param index0 index into storage of item[0]
* @param length number of items in the slice
* @param stride in between successive elements of the new PyBuffer
+ * @throws NullPointerException if <code>storage</code> is null
+ * @throws ArrayIndexOutOfBoundsException if <code>index0</code>, <code>length</code> and
+ * <code>stride</code> are inconsistent with <code>storage.length</code>
* @throws PyException (BufferError) when expectations do not correspond with the type
*/
public Strided1DWritableBuffer(int flags, byte[] storage, int index0, int length, int stride)
- throws PyException {
- // Arguments programme the object directly
- // this();
- this.shape[0] = length;
- this.buf = new BufferPointer(storage, index0);
- this.stride = stride;
- if (stride == 1) {
- // Really this is a simple buffer
- addFeatureFlags(CONTIGUITY);
- }
+ throws ArrayIndexOutOfBoundsException, NullPointerException, PyException {
+ super(storage, index0, length, stride);
addFeatureFlags(WRITABLE);
checkRequestFlags(flags); // Check request is compatible with type
}
@@ -54,7 +48,7 @@
@Override
public void storeAt(byte value, int index) throws IndexOutOfBoundsException, PyException {
- buf.storage[buf.offset + index * stride] = value;
+ storage[index0 + index * stride] = value;
}
/**
@@ -67,18 +61,18 @@
// Data is here in the buffers
int s = srcPos;
- int d = buf.offset + destIndex * stride;
+ int d = index0 + destIndex * stride;
// Strategy depends on whether items are laid end-to-end or there are gaps
if (stride == 1) {
// Straight copy of contiguous bytes
- System.arraycopy(src, srcPos, buf.storage, d, length);
+ System.arraycopy(src, srcPos, storage, d, length);
} else {
// Non-contiguous copy: single byte items
int limit = d + length * stride;
for (; d != limit; d += stride) {
- buf.storage[d] = src[s++];
+ storage[d] = src[s++];
}
}
}
@@ -93,24 +87,11 @@
public PyBuffer getBufferSlice(int flags, int start, int length, int stride) {
if (length > 0) {
- int compStride;
-
- if (stride == 1) {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length);
- // Composite stride is same as original stride
- compStride = this.stride;
- } else {
- // Check the arguments define a slice within this buffer
- checkSlice(start, length, stride);
- // Composite stride is product
- compStride = this.stride * stride;
- }
-
// Translate start relative to underlying buffer
- int compIndex0 = buf.offset + start * this.stride;
+ int compStride= this.stride * stride;
+ int compIndex0 = index0 + start * this.stride;
// Construct a view, taking a lock on the root object (this or this.root)
- return new SlicedView(getRoot(), flags, buf.storage, compIndex0, length, compStride);
+ return new SlicedView(getRoot(), flags, storage, compIndex0, length, compStride);
} else {
// Special case for length==0 where above logic would fail. Efficient too.
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
@@ -1,6 +1,5 @@
package org.python.core.buffer;
-import org.python.core.BufferPointer;
import org.python.core.PyBuffer;
import org.python.core.PyException;
@@ -15,8 +14,8 @@
*/
public class ZeroByteBuffer extends BaseBuffer {
- /** Shared instance of a zero-length buffer. */
- private static final BufferPointer EMPTY_BUF = new BufferPointer(new byte[0]);
+ /** Shared instance of a zero-length storage. */
+ private static final byte[] EMPTY = new byte[0];
/** Array containing a single zero for the length */
protected static final int[] SHAPE = {0};
@@ -33,7 +32,7 @@
*/
public ZeroByteBuffer(int flags, boolean readonly) throws PyException {
super(CONTIGUITY | SIMPLE | (readonly ? 0 : WRITABLE));
- this.buf = EMPTY_BUF; // Wraps empty array
+ this.storage = EMPTY; // Empty array
this.shape = SHAPE; // {0}
this.strides = SimpleBuffer.SIMPLE_STRIDES; // {1}
checkRequestFlags(flags);
@@ -60,7 +59,7 @@
protected int calcIndex(int... indices) throws IndexOutOfBoundsException {
// Bootless dimension check takes precedence (for consistency with other buffers)
checkDimension(indices);
- // This causes all access to the bytes in to throw (since BaseBuffer calls it).
+ // This causes all access to the bytes to throw (since BaseBuffer calls it).
throw new IndexOutOfBoundsException();
}
@@ -81,7 +80,7 @@
*/
@Override
public void copyTo(int srcIndex, byte[] dest, int destPos, int length)
- throws IndexOutOfBoundsException, PyException {
+ throws IndexOutOfBoundsException, PyException {
// Nothing to copy
}
@@ -91,7 +90,7 @@
*/
@Override
public void copyFrom(byte[] src, int srcPos, int destIndex, int length)
- throws IndexOutOfBoundsException, PyException {
+ throws IndexOutOfBoundsException, PyException {
if (length > 0) {
throw new IndexOutOfBoundsException();
}
@@ -132,6 +131,17 @@
}
/**
+ * {@inheritDoc}
+ * <p>
+ * The implementation in <code>ZeroByteBuffer</code> efficiently returns an empty buffer.
+ */
+ @Override
+ public Pointer getBuf() {
+ // Has to be new because the client is allowed to manipulate the contents.
+ return new Pointer(EMPTY, 0);
+ }
+
+ /**
* For a ZeroByteBuffer, it's the empty string.
*/
@Override
@@ -175,5 +185,5 @@
// We have to release the root too if ours was final.
root.release();
}
- }
+ }
}
diff --git a/src/org/python/core/io/TextIOBase.java b/src/org/python/core/io/TextIOBase.java
--- a/src/org/python/core/io/TextIOBase.java
+++ b/src/org/python/core/io/TextIOBase.java
@@ -5,7 +5,6 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
-import org.python.core.BufferPointer;
import org.python.core.BufferProtocol;
import org.python.core.Py;
import org.python.core.PyArray;
@@ -13,7 +12,6 @@
import org.python.core.PyBuffer;
import org.python.core.PyObject;
import org.python.core.PyString;
-import org.python.core.util.StringUtil;
/**
* Base class for text I/O.
@@ -98,7 +96,7 @@
/**
* Read into the given PyObject that implements the Jython buffer API (with write access) or is
* a PyArray.
- *
+ *
* @param buf a PyObject compatible with the buffer API
* @return the amount of data read as an int
*/
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
@@ -41,7 +41,7 @@
*/
public class PyBufferTest extends TestCase {
- /** Control amount of output. Instance variable so can be adjusted temporarily per test. */
+ /** Control amount of output. Instance variable so can be adjusted temporarily in test. */
protected int verbosity = 0;
/**
@@ -514,7 +514,7 @@
// Check changed part of destination
assertBytesEqual("copyTo(slice) incorrect", test.material.bytes, srcIndex,
- actual, destPos, length);
+ length, actual, destPos);
if (destPos > 0) {
assertEquals("data before destination", BLANK, actual[destPos - 1]);
}
@@ -538,7 +538,7 @@
// Check changed part of destination
assertBytesEqual("copyTo(slice) incorrect", test.material.bytes, srcIndex,
- actual, destPos, length);
+ length, actual, destPos);
if (destPos > 0) {
assertEquals("data before destination", BLANK, actual[destPos - 1]);
}
@@ -860,25 +860,6 @@
}
/**
- * Check that reusable PyBuffer is re-used, and that non-reusable PyBuffer is not re-used.
- *
- * @param subject
- */
- private void checkReusable(BufferProtocol subject, PyBuffer previous, PyBuffer latest) {
- assertNotNull("Re-used PyBuffer reference null", latest);
- if (subject instanceof PyByteArray) {
- // Re-use prohibited because might have resized while released
- assertFalse("PyByteArray buffer reused unexpectedly", latest == previous);
- } else if (subject instanceof TestableExporter && !((TestableExporter)subject).reusable) {
- // Special test case where re-use prohibited
- assertFalse("PyBuffer reused unexpectedly", latest == previous);
- } else {
- // Other types of TestableExporter and PyString all re-use
- assertTrue("PyBuffer not re-used as expected", latest == previous);
- }
- }
-
- /**
* Test method for {@link org.python.core.PyBuffer#getBufferSlice(int, int, int, int)}.
*/
public void testGetBufferSliceWithStride() {
@@ -968,7 +949,7 @@
int flags = test.readonly ? PyBUF.SIMPLE : PyBUF.SIMPLE + PyBUF.WRITABLE;
PyBuffer view = test.subject.getBuffer(flags);
- BufferPointer bp = view.getBuf();
+ PyBuffer.Pointer bp = view.getBuf();
assertBytesEqual("buffer does not match reference", test.material.bytes, bp);
} else {
@@ -977,7 +958,7 @@
PyBuffer view = test.subject.getBuffer(flags);
stride = view.getStrides()[0]; // Just possibly != test.strides when length<=1
- BufferPointer bp = view.getBuf();
+ PyBuffer.Pointer bp = view.getBuf();
assertBytesEqual("buffer does not match reference", test.material.bytes, bp, stride);
}
@@ -1004,9 +985,8 @@
}
// Get pointer and check contents for correct data
- BufferPointer bp = view.getPointer(i);
- int stride = view.getStrides()[0];
- assertBytesEqual("getPointer value", exp, bp, stride);
+ PyBuffer.Pointer bp = view.getPointer(i);
+ assertBytesEqual("getPointer value", exp, bp);
}
}
}
@@ -1031,11 +1011,10 @@
exp[j] = bytes[p + j];
}
- // Get pointer and check contents for correct data
+ // Get pointer and check contents for data matching exp
index[0] = i;
- BufferPointer bp = view.getPointer(index);
- assertBytesEqual("getPointer value", exp, bp.storage, bp.offset);
-// assertEquals("getPointer size wrong", itemsize, bp.size);
+ PyBuffer.Pointer bp = view.getPointer(index);
+ assertBytesEqual("getPointer value", exp, bp);
}
// Check 2D index throws
@@ -1225,12 +1204,6 @@
return false;
}
- /**
- * Determine whether this object permits it's buffers to re-animate themselves. If not, a
- * call to getBuffer on a released buffer should not return the same buffer.
- */
- public boolean reusable = true;
-
}
/**
@@ -1271,15 +1244,12 @@
}
/**
- * A class to act as an exporter that uses the SimpleBuffer. This permits testing abstracted
- * from the Jython interpreter.
- * <p>
- * The exporter shares a single exported buffer between all consumers and needs to take any
- * action immediately when that buffer is finally released. You are most likely to use this
- * approach with an exporting object type that modifies its behaviour while there are active
- * exports, but where it is worth avoiding the cost of duplicate buffers. This is the case with
- * PyByteArray, which prohibits operations that would resize it, while there are outstanding
- * exports.
+ * A class to act as an exporter that uses the SimpleBuffer. The exporter shares a single
+ * exported buffer between all consumers and needs to take any action immediately when that
+ * buffer is finally released. You are most likely to use this approach with an exporting object
+ * type that modifies its behaviour while there are active exports, but where it is worth
+ * avoiding the cost of duplicate buffers. This is the case with PyByteArray, which prohibits
+ * operations that would resize it, while there are outstanding exports.
*/
static class SimpleWritableExporter extends TestableExporter {
@@ -1292,7 +1262,6 @@
*/
public SimpleWritableExporter(byte[] storage) {
this.storage = storage;
- reusable = false;
}
@Override
@@ -1469,7 +1438,7 @@
* @param expected expected byte array
* @param bp result to test
*/
- static void assertBytesEqual(String message, byte[] expected, BufferPointer bp) {
+ static void assertBytesEqual(String message, byte[] expected, PyBuffer.Pointer bp) {
assertBytesEqual(message, expected, bp, 1);
}
@@ -1482,19 +1451,24 @@
* @param bp result to test
* @param stride in the storage array
*/
- static void assertBytesEqual(String message, byte[] expected, BufferPointer bp, int stride) {
- assertBytesEqual(message, expected, 0, bp.storage, bp.offset, expected.length, stride);
+ 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.
*
+ * @param message to issue on failure
* @param expected expected byte array
- * @param bp result to test
+ * @param expectedStart where to start the comparison in expected
+ * @param n number of bytes to test
+ * @param bb result to test
+ * @param stride in the storage array
*/
- static void assertBytesEqual(byte[] expected, BufferPointer bp) {
- assertBytesEqual("", expected, bp);
+ 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);
}
/**
@@ -1506,8 +1480,8 @@
* @param actual result to test
*/
static void assertBytesEqual(String message, byte[] expected, byte[] actual) {
- assertEquals(message, expected.length, actual.length);
- assertBytesEqual(message, expected, 0, actual, 0, expected.length, 1);
+ assertEquals(message + " (array size)", expected.length, actual.length);
+ assertBytesEqual(message, expected, 0, expected.length, actual, 0, 1);
}
/**
@@ -1520,7 +1494,7 @@
* @param actualStart where to start the comparison in actual
*/
static void assertBytesEqual(String message, byte[] expected, byte[] actual, int actualStart) {
- assertBytesEqual(message, expected, 0, actual, actualStart, expected.length, 1);
+ assertBytesEqual(message, expected, 0, expected.length, actual, actualStart, 1);
}
/**
@@ -1530,14 +1504,13 @@
* @param message to issue on failure
* @param expected expected byte array
* @param expectedStart where to start the comparison in expected
+ * @param n number of bytes to test
* @param actual result to test
* @param actualStart where to start the comparison in actual
- * @param n number of bytes to test
*/
- static void assertBytesEqual(String message, byte[] expected, int expectedStart, byte[] actual,
- int actualStart, int n) {
-
- assertBytesEqual(message, expected, expectedStart, actual, actualStart, n, 1);
+ static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n,
+ byte[] actual, int actualStart) {
+ assertBytesEqual(message, expected, expectedStart, n, actual, actualStart, 1);
}
/**
@@ -1547,13 +1520,13 @@
* @param message to issue on failure
* @param expected expected byte array
* @param expectedStart where to start the comparison in expected
+ * @param n number of bytes to test
* @param actual result to test
* @param actualStart where to start the comparison in actual
- * @param n number of bytes to test
* @param stride spacing of bytes in actual array
*/
- static void assertBytesEqual(String message, byte[] expected, int expectedStart, byte[] actual,
- int actualStart, int n, int stride) {
+ static void assertBytesEqual(String message, byte[] expected, int expectedStart, int n,
+ byte[] actual, int actualStart, int stride) {
if (actualStart < 0) {
fail(message + " (start<0 in result)");
--
Repository URL: http://hg.python.org/jython
More information about the Jython-checkins
mailing list