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; } } }

Ads help maintain this website.