See More

package org.python.core.buffer; import org.python.core.BufferProtocol; import org.python.core.PyBuffer; import org.python.core.PyException; /** * Buffer API over a zero length, one-dimensional array of one-byte items. The buffer is nominally * writable, but since there is nowhere to write to, any attempt to write or read throws an * IndexOutOfBoundsException. This class exists mostly to represent zero-length arrays, * and particularly, zero-length slices for which implementations of * {@link PyBuffer#getBufferSlice(int, int, int, int)} in any case need special logic. Bulk * operations like {@link #copyTo(byte[], int)}) and {@link #toString()} efficiently do nothing, * instead of calling complicated logic that finally does nothing. */ public class ZeroByteBuffer extends BaseArrayBuffer { /** Shared instance of a zero-length storage. */ private static final byte[] EMPTY = new byte[0]; /** * Construct an instance of a zero-length buffer, choosing whether it should report itself to be * read-only through {@link #isReadonly()} or as having a backing array through * {@link #hasArray()}. These properties are moot, as any attempt to write to the pretended * backing array produces an {@link IndexOutOfBoundsException}, but it is less surprising for * client code that may ask, if the results are customary for the exporting object. * * @param flags consumer requirements * @param obj exporting object (or null) * @param readonly set true if not to be considered writable * @param hasArray set true if to be considered as backed by an array * @throws PyException {@code BufferError} when client expectations do not correspond with the * type */ public ZeroByteBuffer(int flags, BufferProtocol obj, boolean readonly, boolean hasArray) throws PyException { super(EMPTY, CONTIGUITY | (readonly ? 0 : WRITABLE), 0, 0, 1); this.obj = obj; if (!hasArray) { // super() knows we have an array, but this truth is inconvenient here. removeFeatureFlags(AS_ARRAY); } checkRequestFlags(flags); } @Override public int getLen() { return 0; } /** * In a ZeroByteBuffer, the index is always out of bounds. */ @Override public int byteIndex(int index) throws IndexOutOfBoundsException { // This causes all access to the bytes in to throw (since BaseBuffer calls it). throw new IndexOutOfBoundsException(); } /** * In a ZeroByteBuffer, if the dimensions are right, the index is out of bounds anyway. */ @Override 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). throw new IndexOutOfBoundsException(); } /** * {@inheritDoc} *

* In a ZeroByteBuffer, there is simply nothing to copy. */ @Override public void copyTo(byte[] dest, int destPos) throws IndexOutOfBoundsException { // Nothing to copy } /** * {@inheritDoc} *

* In a ZeroByteBuffer, there is simply nothing to copy. */ @Override public void copyTo(int srcIndex, byte[] dest, int destPos, int count) throws IndexOutOfBoundsException, PyException { // Nothing to copy } /** * In a ZeroByteBuffer, there is no room for anything, so this throws unless the source count is * zero. */ @Override public void copyFrom(byte[] src, int srcPos, int destIndex, int count) throws IndexOutOfBoundsException, PyException { if (this.isReadonly()) { throw notWritable(); } else if (count > 0) { throw new IndexOutOfBoundsException(); } } /** * In a ZeroByteBuffer, there is no room for anything, so this throws unless the source count is * zero. */ @Override public void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException { if (this.isReadonly()) { throw notWritable(); } else if (src.getLen() > 0) { throw new IndexOutOfBoundsException(); } } /** * Only a zero-length slice at zero is valid (in which case, the present buffer will do nicely * as a result, with the export count incremented. */ @Override public PyBuffer getBufferSlice(int flags, int start, int count) { if (start == 0 && count <= 0) { return this.getBuffer(flags); } else { throw new IndexOutOfBoundsException(); } } /** * Only a zero-length slice at zero is valid (in which case, the present buffer will do nicely * as a result, with the export count incremented. */ @Override public PyBuffer getBufferSlice(int flags, int start, int count, int stride) { // It can't matter what the stride is since count is zero, or there's an error. return getBufferSlice(flags, start, count); } /** * {@inheritDoc} *

* The implementation in ZeroByteBuffer efficiently returns an empty buffer. */ @SuppressWarnings("deprecation") @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 public String toString() { return ""; } /** * A ZeroByteBuffer.View represents a contiguous subsequence of another * PyBuffer. We don't need it to make slices of the ZeroByteBuffer itself, but it * is useful for making zero-length slices of anything else. Lock-release semantics must still * be observed. In Python, a zero-length slice obtained from the memoryview of a bytearray still * counts as an export from the bytearray. */ static class View extends ZeroByteBuffer { /** The buffer on which this is a slice view */ PyBuffer root; /** * Construct a slice of a ZeroByteBuffer, which it goes without saying is of zero length at * position zero. * * @param root buffer which will be acquired and must be released ultimately * @param flags the request flags of the consumer that requested the slice */ public View(PyBuffer root, int flags) { // Create a new ZeroByteBuffer on who-cares-what byte array super(flags, root.getObj(), root.isReadonly(), root.hasArray()); // But we still have to get a lease on the root PyBuffer this.root = root.getBuffer(FULL_RO); } @Override protected PyBuffer getRoot() { return root; } } }