org.python.core.buffer.Strided1DBuffer Maven / Gradle / Ivy
Show all versions of jython-slim Show documentation
package org.python.core.buffer;
import org.python.core.BufferProtocol;
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 storage
, index0
and count
* properties in the usual way, designating a slice (or all) of a byte array, but also a
* stride
property (equal to getStrides()[0]
).
*
* Let the underlying buffer be the byte array u(i) for i=0..N-1, let x be the
* Strided1DBuffer
, and let the stride be p. The storage works as follows.
* Designate by x(j), for j=0..L-1, the byte at index j, that is, the byte
* retrieved by x.byteAt(j)
. Thus, we store x(j) at u(a+pj), that is,
* x(0) = u(a). When we construct such a buffer, we have to supply a =
* index0
, L = count
, and p = stride
as the
* constructor arguments. The last item in the slice x(L-1) is stored at u(a+p(L-1)).
* For the simple case of positive stride, constructor argument index0
is the low index
* of the range occupied by the data. When the stride is negative, that is to say p<0, and
* L>1, this will be to the left of u(a), and 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 N-1 inclusive, a rule enforced by the constructors
* (unless L=0, when it is assumed no array access will take place).
*
* 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
* create the memoryview
that is returned as an extended slice of a
* memoryview
.
*/
public class Strided1DBuffer extends BaseArrayBuffer {
/**
* Step size in the underlying buffer essential to correct translation of an index (or indices)
* into an index into the storage. The value is returned by {@link #getStrides()} is an array
* with this as the only element.
*/
protected int stride;
/**
* Provide an instance of Strided1DBuffer
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).
*
* The sub-class constructor should check that the intended access is compatible with this
* object by calling {@link #checkRequestFlags(int)}. (See the source of
* {@link Strided1DWritableBuffer} for an example of this use.)
*
* @param obj exporting object (or null
)
* @param storage raw byte array containing exported data
* @param index0 index into storage of item[0]
* @param count number of items in the slice
* @param stride in between successive elements of the new PyBuffer
* @throws NullPointerException if storage
is null
* @throws ArrayIndexOutOfBoundsException if index0
, count
and
* stride
are inconsistent with storage.length
*/
protected Strided1DBuffer(BufferProtocol obj, byte[] storage, int index0, int count, int stride)
throws ArrayIndexOutOfBoundsException, NullPointerException {
super(storage, STRIDES, index0, count, stride);
this.obj = obj;
this.stride = stride; // Between items
if (count == 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 + count; // Last byte of item[L-1] + 1
addFeatureFlags(CONTIGUITY);
} else if (stride > 1) {
lo = index0; // First byte of item[0]
hi = index0 + (count - 1) * stride + 1; // Last byte of item[L-1] + 1
} else {
hi = index0 + 1; // Last byte of item[0] + 1
lo = index0 + (count - 1) * stride; // First byte of item[L-1]
}
// Check indices using "all non-negative" trick
if ((count | lo | (storage.length - lo) | hi | (storage.length - hi)) < 0) {
throw new ArrayIndexOutOfBoundsException();
}
}
}
/**
* Provide an instance of Strided1DBuffer
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 byteAt(i)
will be equal to storage[index0+stride*i]
(whatever
* the sign of stride
), valid for 0<=i<count
. The constructor
* checks that all these indices lie within the storage
array (unless
* count=0
).
*
* The constructed PyBuffer
meets the consumer's expectations as expressed in the
* flags
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 storage
array, the lowest and highest index, is not
* explicitly passed, but is implicit in index0
, count
and
* stride
. The constructor checks that these indices lie within the
* storage
array (unless count=0
).
*
* @param flags consumer requirements
* @param obj exporting object (or null
)
* @param storage raw byte array containing exported data
* @param index0 index into storage of item[0]
* @param count number of items in the slice
* @param stride byte-index distance from one element to the next in the new PyBuffer
* @throws NullPointerException if storage
is null
* @throws ArrayIndexOutOfBoundsException if index0
, count
and
* stride
are inconsistent with storage.length
* @throws PyException {@code BufferError} when expectations do not correspond with the type
*/
public Strided1DBuffer(int flags, BufferProtocol obj, byte[] storage, int index0, int count, int stride)
throws ArrayIndexOutOfBoundsException, NullPointerException, PyException {
this(obj, storage, index0, count, stride);
checkRequestFlags(flags); // Check request is compatible with type
}
@Override
public boolean isReadonly() {
return true;
}
@Override
public final int byteIndex(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= shape[0]) {
throw new IndexOutOfBoundsException();
}
return index0 + index * stride;
}
/**
* {@inheritDoc}
*
* Strided1DBuffer
provides an implementation for slicing already-strided bytes in
* one dimension. In that case, x(i) = u(r+ip) for i = 0..L-1 where u is the
* underlying buffer, and r, p and L are the start, stride and count with
* which x was created from u. Thus y(k) = u(r+sp+kmp), that is, the
* composite index0
is r+sp and the composite stride
is
* mp.
*/
@Override
public PyBuffer getBufferSlice(int flags, int start, int count, int stride) {
if (count > 0) {
// Translate start relative to underlying buffer
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, storage, compIndex0, count, compStride);
} else {
// Special case for count==0 where above logic would fail. Efficient too.
return new ZeroByteBuffer.View(getRoot(), flags);
}
}
@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.
checkDimension(indices.length);
return getPointer(indices[0]);
}
/**
* A Strided1DBuffer.SlicedView
represents a non-contiguous subsequence of a simple
* buffer.
*/
static class SlicedView extends Strided1DBuffer {
/** The buffer on which this is a slice view */
PyBuffer root;
/**
* Construct a slice of a one-dimensional byte buffer.
*
* @param root on which release must be called when this is released
* @param flags consumer requirements
* @param storage raw byte array containing exported data
* @param index0 index into storage of item[0]
* @param count number of items in the sliced view
* @param stride in between successive elements of the new PyBuffer
* @throws PyException {@code BufferError} when expectations do not correspond with the type
*/
public SlicedView(PyBuffer root, int flags, byte[] storage, int index0, int count,
int stride) throws PyException {
// Create a new on the buffer passed in (part of the root)
super(flags, root.getObj(), storage, index0, count, stride);
// Get a lease on the root PyBuffer (read-only)
this.root = root.getBuffer(FULL_RO);
}
@Override
protected PyBuffer getRoot() {
return root;
}
}
}