package org.python.core.buffer;

import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;

import org.python.core.PyBUF;
import org.python.core.PyException;

/**
 * Base implementation of the Buffer API for when the storage implementation is
 * java.nio.ByteBuffer. The description of {@link BaseBuffer} mostly applies. Methods
 * provided or overridden here are appropriate to 1-dimensional arrays, of any item size, backed by
 * a ByteBuffer.
 */
public abstract class BaseNIOBuffer extends Base1DBuffer {

    /**
     * A {@link java.nio.ByteBuffer} (possibly a direct buffer) wrapping the storage that the
     * exporter is sharing with the consumer. The data to be exposed may be only a subset of the
     * bytes in the buffer, defined by the navigation information index0,
     * shape, strides, etc., usually defined in the constructor.
     * 

* Implementations must not adjust the position and limit of storage after * construction. It will generally be a duplicate of (not a reference to) a ByteBuffer held by * the client code. The capacity and backing store are fixed in construction, and the position * will always be {@link #index0}. The limit is always higher than any valid data, and in the * case of a contiguous buffer (with positive stride), is exactly just beyond the last item, so * that a series of ByteBuffer.get operations will yield the data. */ protected ByteBuffer storage; /** * Construct an instance of BaseNIOBuffer in support of a sub-class, specifying the * 'feature flags', or at least a starting set to be adjusted later. Also specify the navigation * ( {@link #index0}, number of elements, and stride. These 'feature flags' are the features of * the buffer exported, not the flags that form the consumer's request. The buffer will be * read-only unless {@link PyBUF#WRITABLE} is set. {@link PyBUF#FORMAT} and * {@link PyBUF#AS_ARRAY} are implicitly added to the feature flags. *

* To complete initialisation, the sub-class normally must call {@link #checkRequestFlags(int)} * passing the consumer's request flags. * * @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 featureFlags bit pattern that specifies the features allowed * @param index0 index into storage of item[0] * @param size number of elements in the view * @param stride byte-index step between successive elements */ protected BaseNIOBuffer(ByteBuffer storage, int featureFlags, int index0, int size, int stride) { super(featureFlags & ~(WRITABLE | AS_ARRAY), index0, size, stride); this.storage = storage; // Deduce other feature flags from the client's ByteBuffer if (!storage.isReadOnly()) { addFeatureFlags(WRITABLE); } if (storage.hasArray()) { addFeatureFlags(AS_ARRAY); } } @Override protected byte byteAtImpl(int byteIndex) throws IndexOutOfBoundsException { return storage.get(byteIndex); } @Override protected void storeAtImpl(byte value, int byteIndex) throws PyException { try { storage.put(byteIndex, value); } catch (ReadOnlyBufferException rbe) { throw notWritable(); } } @Override public int byteIndex(int... indices) throws IndexOutOfBoundsException { // BaseBuffer implementation can be simplified since if indices.length!=1 we error. checkDimension(indices.length); // throws if != 1 return byteIndex(indices[0]); } /** * {@inheritDoc} *

* The default implementation in BaseNIOBuffer deals with the general * one-dimensional case of arbitrary item size and stride. */ @Override public void copyTo(int srcIndex, byte[] dest, int destPos, int count) throws IndexOutOfBoundsException { // Wrap the destination, taking care to reflect the necessary range we shall write. ByteBuffer destBuf = ByteBuffer.wrap(dest, destPos, count * getItemsize()); copyTo(srcIndex, destBuf, count); } /** * Copy all items in this buffer into a ByteBuffer, starting at its current * position. * * @param dest destination buffer * @throws BufferOverflowException * @throws ReadOnlyBufferException */ // XXX Should this become part of the PyBUffer interface? public void copyTo(ByteBuffer dest) throws BufferOverflowException, ReadOnlyBufferException { // Note shape[0] is the number of items in the buffer copyTo(0, dest, shape[0]); } /** * Copy a specified number of items from a particular location in this buffer into a * ByteBuffer, starting at its current position. . * * @param srcIndex index of the first item to copy * @param dest destination buffer * @param count number of items to copy * @throws BufferOverflowException * @throws ReadOnlyBufferException * @throws IndexOutOfBoundsException */ // XXX Should this become part of the PyBuffer interface? protected void copyTo(int srcIndex, ByteBuffer dest, int count) throws BufferOverflowException, ReadOnlyBufferException, IndexOutOfBoundsException { if (count > 0) { ByteBuffer src = getNIOByteBuffer(); int pos = byteIndex(srcIndex); // Pick up attributes necessary to choose an efficient copy strategy int itemsize = getItemsize(); int stride = getStrides()[0]; // Strategy depends on whether items are laid end-to-end contiguously or there are gaps if (stride == itemsize) { // stride == itemsize: straight copy of contiguous bytes src.limit(pos + count * itemsize).position(pos); dest.put(src); } else if (itemsize == 1) { // Non-contiguous copy: single byte items for (int i = 0; i < count; i++) { src.position(pos); dest.put(src.get()); pos += stride; } } else { // Non-contiguous copy: each time, copy itemsize bytes then skip for (int i = 0; i < count; i++) { src.limit(pos + itemsize).position(pos); dest.put(src); pos += stride; } } } } /** * {@inheritDoc} *

* The default implementation in BaseNIOBuffer deals with the general * one-dimensional case of arbitrary item size and stride. */ @Override public void copyFrom(byte[] src, int srcPos, int destIndex, int count) throws IndexOutOfBoundsException, PyException { // Wrap the source, taking care to reflect the range we shall read. ByteBuffer srcBuf = ByteBuffer.wrap(src, srcPos, count * getItemsize()); copyFrom(srcBuf, destIndex, count); } /** * Copy a specified number of items from a ByteBuffer into this buffer at a * particular location. * * @param src source ByteBuffer * @param destIndex starting item-index in the destination (i.e. this) * @param count number of items to copy in * @throws IndexOutOfBoundsException if access out of bounds in source or destination * @throws PyException {@code TypeError} if read-only buffer */ // XXX Should this become part of the PyBUffer interface? protected void copyFrom(ByteBuffer src, int destIndex, int count) throws IndexOutOfBoundsException, PyException { checkWritable(); if (count > 0) { ByteBuffer dest = getNIOByteBuffer(); int pos = byteIndex(destIndex); // Pick up attributes necessary to choose an efficient copy strategy int itemsize = getItemsize(); int stride = getStrides()[0]; int skip = stride - itemsize; int size = getSize(); // Check indexes in destination (this) using the "all non-negative" trick if ((destIndex | count | size - (destIndex + count)) < 0) { throw new IndexOutOfBoundsException(); } // Strategy depends on whether items are laid end-to-end or there are gaps if (skip == 0) { // Straight copy of contiguous bytes dest.position(pos); dest.put(src); } else if (itemsize == 1) { // Non-contiguous copy: single byte items for (int i = 0; i < count; i++) { dest.position(pos); dest.put(src.get()); // Next byte written will be here pos += stride; } } else { // Non-contiguous copy: each time, copy itemsize bytes at a time for (int i = 0; i < count; i++) { dest.position(pos); // Delineate the next itemsize bytes in the src src.limit(src.position() + itemsize); dest.put(src); // Next byte written will be here pos += stride; } } } } @Override protected ByteBuffer getNIOByteBufferImpl() { return storage.duplicate(); } @SuppressWarnings("deprecation") @Override public Pointer getBuf() { checkHasArray(); return new Pointer(storage.array(), index0); } }

Ads help maintain this website.