package org.python.core.buffer;
import java.nio.ByteBuffer;
import org.python.core.BufferProtocol;
import org.python.core.PyBuffer;
import org.python.core.PyException;
/**
* Read-only buffer API over a one-dimensional java.nio.ByteBuffer of one-byte items,
* that are evenly-spaced in that store. The buffer has storage, index0
* and length 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 sequence u(i) for i=0..N-1, let x be
* the Strided1DNIOBuffer, 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 Strided1DNIOBuffer extends BaseNIOBuffer {
/**
* 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 Strided1DNIOBuffer 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 the ByteBuffer wrapping the exported object state. NOTE: this
* PyBuffer keeps a reference and may manipulate the position, mark and
* limit hereafter. Use {@link ByteBuffer#duplicate()} to give it an isolated copy.
* @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 Strided1DNIOBuffer(BufferProtocol obj, ByteBuffer 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
int cap = storage.capacity();
if ((count | lo | (cap - lo) | hi | (cap - hi)) < 0) {
throw new ArrayIndexOutOfBoundsException();
}
}
// Deduce feature flags from the client's ByteBuffer
if (!storage.isReadOnly()) {
addFeatureFlags(WRITABLE);
}
if (storage.hasArray()) {
addFeatureFlags(AS_ARRAY);
}
}
/**
* Provide an instance of Strided1DNIOBuffer on a particular {@link ByteBuffer}
* 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.get(index0+stride*i) (whatever the sign of stride), valid
* for 0<=i<count. The constructor checks that all these indices lie within
* the storage (unless count=0). No reference will be kept to the
* ByteBuffer passed in. (It is duplicated.)
*
* 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 ByteBuffer wrapping 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
* @throws PyException {@code BufferError} when expectations do not correspond with the type
*/
public Strided1DNIOBuffer(int flags, BufferProtocol obj, ByteBuffer storage, int index0,
int count, int stride) throws ArrayIndexOutOfBoundsException, NullPointerException,
PyException {
this(obj, storage.duplicate(), index0, count, stride);
checkRequestFlags(flags); // Check request is compatible with type
}
@Override
public final int byteIndex(int index) throws IndexOutOfBoundsException {
return index0 + index * stride;
}
/**
* {@inheritDoc}
*
* Strided1DNIOBuffer 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);
}
}
/**
* A Strided1DNIOBuffer.SlicedView represents a non-contiguous subsequence of a
* simple buffer.
*/
static class SlicedView extends Strided1DNIOBuffer {
/** 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 ByteBuffer wrapping exported data (no reference kept)
* @param index0 index into storage of item[0]
* @param count the 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, ByteBuffer storage, int index0, int count,
int stride) throws PyException {
// Create a new slice 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;
}
}
}
Ads help maintain this website.