package org.python.core.buffer;

import java.nio.ByteBuffer;

import org.python.core.BufferProtocol;
import org.python.core.PyBuffer;
import org.python.core.util.StringUtil;

/**
 * Buffer API that appears to be a one-dimensional array of one-byte items providing read-only API,
 * but which is actually backed by a Java String. Some of the buffer API absolutely needs access to
 * the data as a byte array (those parts that involve a {@link java.nio.ByteBuffer} or
 * {@link org.python.core.PyBuffer.Pointer} result), and therefore this class must create a byte array from the
 * String for them. However, it defers creation of a byte array until that part of the API is
 * actually used. Where possible, this class overrides those methods in SimpleBuffer that would
 * otherwise access the byte array attribute to use the String instead.
 */
public class SimpleStringBuffer extends SimpleBuffer {

    /**
     * The string backing this PyBuffer. A substitute for {@link #buf} until we can no longer avoid
     * creating it.
     */
    private String bufString;

    /**
     * Provide an instance of SimpleStringBuffer meeting the consumer's expectations as expressed in
     * the flags argument.
     *
     * @param flags consumer requirements
     * @param obj exporting object (or null)
     * @param bufString storing the implementation of the object
     */
    public SimpleStringBuffer(int flags, BufferProtocol obj, String bufString) {
        /*
         * Leaving storage=null is ok because we carefully override every method that uses it,
         * deferring creation of the storage byte array until we absolutely must have one.
         */
        super(obj, null, 0, bufString.length());
        // Save the backing string
        this.bufString = bufString;
        // Check request is compatible with type
        checkRequestFlags(flags);
    }

    /**
     * {@inheritDoc}
     * 

* This method uses {@link String#length()} rather than create an actual byte buffer. */ @Override public int getLen() { // Avoid creating buf by using String.length return bufString.length(); } /** * {@inheritDoc} *

* This method uses {@link String#charAt(int)} rather than create an actual byte buffer. */ @Override public final byte byteAtImpl(int index) { return (byte)bufString.charAt(index); } /** * {@inheritDoc} *

* In SimpleStringBuffer we can simply return the argument. */ @Override public final int byteIndex(int index) { // We do not check the index because String will do it for us. return index; } /** * {@inheritDoc} *

* This method uses {@link String#charAt(int)} rather than create an actual byte buffer. */ @Override public void copyTo(int srcIndex, byte[] dest, int destPos, int count) throws IndexOutOfBoundsException { // Avoid creating buf by using String.charAt int endIndex = srcIndex + count, p = destPos; for (int i = srcIndex; i < endIndex; i++) { dest[p++] = (byte)bufString.charAt(i); } } /** * {@inheritDoc} *

* The SimpleStringBuffer implementation avoids creation of a byte buffer. */ @Override public PyBuffer getBufferSlice(int flags, int start, int count) { if (count > 0) { // The new string content is just a sub-string. return new SimpleStringView(getRoot(), flags, bufString.substring(start, start + count)); } else { // Special case for count==0 where start out of bounds sometimes raises exception. return new ZeroByteBuffer.View(getRoot(), flags); } } /** * {@inheritDoc} *

* The SimpleStringBuffer implementation creates an actual byte buffer. */ @Override public PyBuffer getBufferSlice(int flags, int start, int count, int stride) { if (stride == 1) { // Unstrided slice of a SimpleStringBuffer is itself a SimpleStringBuffer. return getBufferSlice(flags, start, count); } else { // Force creation of the actual byte array from the String. ensureHaveBytes(); // Now we are effectively a SimpleBuffer, return the strided view. return super.getBufferSlice(flags, start, count, stride); } } @Override protected ByteBuffer getNIOByteBufferImpl() { // Force creation of the actual byte array from the String. ensureHaveBytes(); // The buffer spans the whole storage, which may include data not in the view ByteBuffer b = ByteBuffer.wrap(storage); // Return as read-only. return b.asReadOnlyBuffer(); } /** * This method creates an actual byte array from the underlying String if none yet exists. */ private void ensureHaveBytes() { if (storage == null) { // We can't avoid creating the byte array any longer (index0 already correct) storage = StringUtil.toBytes(bufString); } } /** * {@inheritDoc} *

* This method creates an actual byte array from the underlying String if none yet exists. */ @SuppressWarnings("deprecation") @Override public Pointer getBuf() { ensureHaveBytes(); return super.getBuf(); } /** * {@inheritDoc} *

* This method creates an actual byte array from the underlying String if none yet exists. */ @SuppressWarnings("deprecation") @Override public Pointer getPointer(int index) { ensureHaveBytes(); return super.getPointer(index); } /** * {@inheritDoc} *

* This method creates an actual byte array from the underlying String if none yet exists. */ @SuppressWarnings("deprecation") @Override public Pointer getPointer(int... indices) { ensureHaveBytes(); return super.getPointer(indices); } /** * The toString() method of a SimpleStringBuffer simply produces the * underlying String. */ @Override public String toString() { return bufString; } /** * A SimpleStringBuffer.SimpleStringView represents a contiguous subsequence of * another SimpleStringBuffer. */ static class SimpleStringView extends SimpleStringBuffer { /** The buffer on which this is a slice view */ PyBuffer root; /** * Construct a slice of a SimpleStringBuffer. * * @param root buffer which will be acquired and must be released ultimately * @param flags the request flags of the consumer that requested the slice * @param buf becomes the buffer of bytes for this object */ public SimpleStringView(PyBuffer root, int flags, String bufString) { // Create a new SimpleStringBuffer on the string passed in super(flags, root.getObj(), bufString); // Get a lease on the root PyBuffer this.root = root.getBuffer(FULL_RO); } @Override protected PyBuffer getRoot() { return root; } } }

Ads help maintain this website.