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.