public abstract class BaseBuffer extends Object implements PyBuffer
Where provided, the buffer access methods are appropriate to 1-dimensional arrays where the units are single bytes, stored contiguously. Sub-classes that deal with N-dimensional arrays, non-contiguous storage and items that are not single bytes must override the default implementations.
This base implementation is writable only if PyBUF.WRITABLE
is in the feature flags
passed to the constructor. Otherwise, all methods for write access raise a
BufferError
read-only exception and isReadonly()
returns true
.
Sub-classes can follow the same pattern, setting PyBUF.WRITABLE
in the constructor and,
if they have to, overriding the operations that write (storeAt
and
copyFrom
). The recommended pattern is:
if (isReadonly()) { throw notWritable(); } // ... implementation of the write operationAnother approach, used in the standard library, is to have distinct classes for the writable and read-only variants. The implementors of simple buffers will find it efficient to override the generic access methods to which performance might be sensitive, with a calculation specific to their actual type.
At the time of writing, only one-dimensional buffers of item size one are used in the Jython core.
PyBuffer.Pointer
ANY_CONTIGUOUS, AS_ARRAY, C_CONTIGUOUS, CONTIG, CONTIG_RO, CONTIGUITY, F_CONTIGUOUS, FORMAT, FULL, FULL_RO, INDIRECT, IS_C_CONTIGUOUS, IS_F_CONTIGUOUS, MAX_NDIM, NAVIGATION, ND, RECORDS, RECORDS_RO, SIMPLE, STRIDED, STRIDED_RO, STRIDES, WRITABLE
Modifier and Type | Method and Description |
---|---|
byte |
byteAt(int... indices)
Return the byte indexed from an N-dimensional buffer with item size one.
|
byte |
byteAt(int index)
Return the byte indexed from a one-dimensional buffer with item size one.
|
void |
close()
An alias for
PyBuffer.release() to satisfy AutoCloseable . |
void |
copyFrom(byte[] src,
int srcPos,
int destIndex,
int length)
Copy bytes from a slice of a (Java) byte array into the buffer.
|
void |
copyFrom(PyBuffer src)
Copy the whole of another PyBuffer into this buffer.
|
void |
copyTo(byte[] dest,
int destPos)
Copy the contents of the buffer to the destination byte array.
|
void |
copyTo(int srcIndex,
byte[] dest,
int destPos,
int length)
Copy a simple slice of the buffer to the destination byte array, defined by a starting index
and length in the source buffer.
|
PyBuffer.Pointer |
getBuf()
Return a structure describing the slice of a byte array that holds the data being exported to
the consumer.
|
PyBuffer |
getBuffer(int flags)
Method by which the consumer requests the buffer from the exporter.
|
BaseBuffer |
getBufferAgain(int flags)
Allow an exporter to re-use a BaseBytes even if it has been "finally" released.
|
PyBuffer |
getBufferSlice(int flags,
int start,
int length)
Equivalent to
PyBuffer.getBufferSlice(int, int, int, int) with stride 1. |
String |
getFormat()
A format string in the language of Python structs describing how the bytes of each item
should be interpreted.
|
int |
getItemsize()
The number of units (bytes) stored in each indexable item.
|
int |
getLen()
The total number of units (bytes) stored, which will be the product of the elements of the
shape array, and the item size in units. |
int |
getNdim()
The number of dimensions to the buffer.
|
ByteBuffer |
getNIOByteBuffer()
Obtain a
ByteBuffer giving access to the bytes that hold the data being
exported to the consumer. |
PyBuffer.Pointer |
getPointer(int... indices)
Return a structure describing the position in a byte array of a single item from the data
being exported to the consumer, in the case that array may be multi-dimensional.
|
PyBuffer.Pointer |
getPointer(int index)
Return a structure describing the position in a byte array of a single item from the data
being exported to the consumer.
|
int[] |
getShape()
An array reporting the size of the buffer, considered as a multidimensional array, in each
dimension and (by its length) giving the number of dimensions.
|
int[] |
getStrides()
The
strides array gives the distance in the storage array between adjacent items
(in each dimension). |
int[] |
getSuboffsets()
The
suboffsets array is a further part of the support for interpreting the
buffer as an n-dimensional array of items, where the array potentially uses indirect
addressing (like a real Java array of arrays, in fact). |
boolean |
hasArray()
Determine whether the exporter is able to offer direct access to the exported storage as a
Java byte array (through the API that involves class
PyBuffer.Pointer ), or only supports the
abstract API. |
int |
intAt(int... indices)
Return the unsigned byte value indexed from an N-dimensional buffer with item size one.
|
int |
intAt(int index)
Return the unsigned byte value indexed from a one-dimensional buffer with item size one.
|
boolean |
isContiguous(char order)
Enquire whether the array is represented contiguously in the backing storage, according to C
or Fortran ordering.
|
boolean |
isReadonly()
Determine whether the consumer is entitled to write to the exported storage.
|
boolean |
isReleased()
True only if the buffer has been released with (the required number of calls to)
PyBuffer.release() or some equivalent operation. |
void |
release()
A buffer is (usually) a view onto to the internal state of an exporting object, and that
object may have to restrict its behaviour while the buffer exists.
|
void |
storeAt(byte value,
int... indices)
Store the given byte at the indexed location in of an N-dimensional buffer with item size
one.
|
void |
storeAt(byte value,
int index)
Store the given byte at the indexed location in of a one-dimensional buffer with item size
one.
|
String |
toString()
The toString() method of a buffer reproduces the values in the buffer (as unsigned integers)
as the character codes of a
String . |
equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
getBufferSlice
public boolean isReadonly()
PyBUF
isReadonly
in interface PyBUF
public boolean hasArray()
PyBuffer
PyBuffer.Pointer
), or only supports the
abstract API. See also PyBUF.AS_ARRAY
.public int getNdim()
PyBUF
shape
array. The actual storage may be a linear array, but this is the number of dimensions in the
interpretation that the exporting object gives the data.public int[] getShape()
PyBUF
shape
array is
always returned (difference from CPython).public int getLen()
shape
array, and the item size in units.
The default implementation in BaseBuffer
deals with the general one-dimensional
case, with any item size and stride.
public byte byteAt(int index) throws IndexOutOfBoundsException
PyBuffer
itemsize>1
.byteAt
in interface PyBuffer
index
- to retrieve fromIndexOutOfBoundsException
public int intAt(int index) throws IndexOutOfBoundsException
PyBuffer
itemsize>1
.intAt
in interface PyBuffer
index
- to retrieve from=0xff & byteAt(index)
IndexOutOfBoundsException
public void storeAt(byte value, int index) throws IndexOutOfBoundsException, PyException
PyBuffer
itemsize>1
.storeAt
in interface PyBuffer
value
- to storeindex
- to locationIndexOutOfBoundsException
PyException
public byte byteAt(int... indices) throws IndexOutOfBoundsException
PyBuffer
itemsize>1
.byteAt
in interface PyBuffer
indices
- specifying location to retrieve fromIndexOutOfBoundsException
public int intAt(int... indices) throws IndexOutOfBoundsException
PyBuffer
itemsize>1
.intAt
in interface PyBuffer
indices
- specifying location to retrieve from=0xff & byteAt(index)
IndexOutOfBoundsException
public void storeAt(byte value, int... indices) throws IndexOutOfBoundsException, PyException
PyBuffer
itemsize>1
.storeAt
in interface PyBuffer
value
- to storeindices
- specifying location to store atIndexOutOfBoundsException
PyException
public void copyTo(byte[] dest, int destPos) throws IndexOutOfBoundsException
PyBUF.getLen()
, and the order is the storage order in the exporter.
(Note: Correct ordering for multidimensional arrays, including those with indirection needs
further study.)
The default implementation in BaseBuffer
deals with the general one-dimensional
case of arbitrary item size and stride.
copyTo
in interface PyBuffer
dest
- destination byte arraydestPos
- index in the destination array of the byte [0]IndexOutOfBoundsException
- if the destination cannot hold itpublic void copyTo(int srcIndex, byte[] dest, int destPos, int length) throws IndexOutOfBoundsException
length*itemsize
bytes will be occupied
in the destination.
The default implementation in BaseBuffer
deals with the general one-dimensional
case of arbitrary item size and stride.
copyTo
in interface PyBuffer
srcIndex
- starting index in the source bufferdest
- destination byte arraydestPos
- index in the destination array of the item [0,...]length
- number of items to copyIndexOutOfBoundsException
- if access out of bounds in source or destinationpublic void copyFrom(byte[] src, int srcPos, int destIndex, int length) throws IndexOutOfBoundsException, PyException
length*itemsize
bytes will be read from the source.
The default implementation in BaseBuffer
deals with the general one-dimensional
case of arbitrary item size and stride.
copyFrom
in interface PyBuffer
src
- source byte arraysrcPos
- location in source of first byte to copydestIndex
- starting index in the destination (i.e. this
)length
- number of bytes to copy inIndexOutOfBoundsException
- if access out of bounds in source or destinationPyException
- (TypeError) if read-only bufferpublic void copyFrom(PyBuffer src) throws IndexOutOfBoundsException, PyException
The default implementation in BaseBuffer
deals with the general one-dimensional
case.
copyFrom
in interface PyBuffer
src
- source bufferIndexOutOfBoundsException
- if access out of bounds in source or destinationPyException
- (TypeError) if read-only bufferpublic PyBuffer getBuffer(int flags)
PyBuffer
PyBuffer.release()
on the buffer it
obtained, since some objects alter their behaviour while buffers are exported.
When a PyBuffer
is the target, the same checks are carried out on the consumer
flags, and a return will normally be a reference to that buffer. A Jython
PyBuffer
keeps count of these re-exports in order to match them with the number
of calls to PyBuffer.release()
. When the last matching release()
arrives it is
considered "final", and release actions may then take place on the exporting object. After
the final release of a buffer, a call to getBuffer
should raise an exception.
getBuffer
in interface BufferProtocol
getBuffer
in interface PyBuffer
flags
- specifying features demanded and the navigational capabilities of the consumerpublic BaseBuffer getBufferAgain(int flags)
BaseBytes
can be re-used even after a final release by consumers,
simply by incrementing the exports
count again: the navigation arrays and the
buffer view of the exporter's state all remain valid. We do not let consumers do this through
the PyBuffer
interface: from their perspective, calling PyBuffer.release()
should mean the end of their access, although we can't stop them holding a reference to the
PyBuffer. Only the exporting object, which handles the implementation type is trusted to know
when re-use is safe.
An exporter will use this method as part of its implementation of
BufferProtocol.getBuffer(int)
. On return from that, the buffer and the exporting
object must then be in effectively the same state as if the buffer had just been
constructed by that method. Exporters that destroy related resources on final release of
their buffer (by overriding releaseAction()
), or permit themselves structural change
invalidating the buffer, must either reconstruct the missing resources or avoid
getBufferAgain
.
public void release()
BufferProtocol.getBuffer(int)
or PyBuffer.getBuffer(int)
should make a
matching call to PyBuffer.release()
. The consumer may be sharing the PyBuffer
with other consumers and the buffer uses the pairing of getBuffer
and
release
to manage the lock on behalf of the exporter. It is an error to make
more than one call to release
for a single call to getBuffer
.
When the final matching release occurs (that is the number of release
calls
equals the number of getBuffer
calls), the implementation here calls
releaseAction()
, which the implementer of a specific buffer type should override if
it needs specific actions to take place.
public void close()
PyBuffer
PyBuffer.release()
to satisfy AutoCloseable
.close
in interface AutoCloseable
close
in interface PyBuffer
public boolean isReleased()
PyBuffer
PyBuffer.release()
or some equivalent operation. The consumer may be sharing the reference
with other consumers and the buffer only achieves the released state when all consumers who
called getBuffer
have called release
.isReleased
in interface PyBuffer
public PyBuffer getBufferSlice(int flags, int start, int length)
PyBuffer
PyBuffer.getBufferSlice(int, int, int, int)
with stride 1.getBufferSlice
in interface PyBuffer
flags
- specifying features demanded and the navigational capabilities of the consumerstart
- index in the current bufferlength
- number of items in the required slicepublic ByteBuffer getNIOByteBuffer()
PyBuffer
ByteBuffer
giving access to the bytes that hold the data being
exported to the consumer. For a one-dimensional contiguous buffer, assuming the following
client code where obj
has type BufferProtocol
:
PyBuffer a = obj.getBuffer(PyBUF.SIMPLE); int itemsize = a.getItemsize(); ByteBuffer bb = a.getNIOBuffer();the item with index
bb.pos()+k
is in the buffer bb
at positions
bb.pos()+k*itemsize
to bb.pos()+(k+1)*itemsize - 1
inclusive. And
if itemsize==1
, the item is simply the byte at position bb.pos()+k
.
The buffer limit is set to the first byte beyond the valid data. A block read or write will
therefore access the contents sequentially.
If the buffer is multidimensional or non-contiguous (strided), the buffer position is still
the (first byte of) the item at index [0]
or [0,...,0]
, and the
limit is one item beyond the valid data. However, it is necessary to navigate bb
using the shape
, strides
and maybe suboffsets
provided
by the API.
getNIOByteBuffer
in interface PyBuffer
public PyBuffer.Pointer getBuf()
PyBuffer
obj
has type BufferProtocol
:
PyBuffer a = obj.getBuffer(PyBUF.SIMPLE); int itemsize = a.getItemsize(); PyBuffer.Pointer b = a.getBuf();the item with index
k
is in the array b.storage
at index
[b.offset + k*itemsize]
to [b.offset + (k+1)*itemsize - 1]
inclusive. And if itemsize==1
, the item is simply the byte
b.storage[b.offset + k]
If the buffer is multidimensional or non-contiguous, storage[offset]
is still
the (first byte of) the item at index [0] or [0,...,0]. However, it is necessary to navigate
b.storage
using the shape
, strides
and maybe
suboffsets
provided by the API.
public PyBuffer.Pointer getPointer(int index) throws IndexOutOfBoundsException
PyBuffer
obj
has type BufferProtocol
:
int k = ... ; PyBuffer a = obj.getBuffer(PyBUF.FULL); int itemsize = a.getItemsize(); PyBuffer.Pointer b = a.getPointer(k);the item with index
k
is in the array b.storage
at index
[b.offset]
to [b.offset + itemsize - 1]
inclusive. And if
itemsize==1
, the item is simply the byte b.storage[b.offset]
Essentially this is a method for computing the offset of a particular index. The client is
free to navigate the underlying buffer b.storage
without respecting these
boundaries.
getPointer
in interface PyBuffer
index
- in the buffer to position the pointerIndexOutOfBoundsException
public PyBuffer.Pointer getPointer(int... indices) throws IndexOutOfBoundsException
PyBuffer
obj
has type BufferProtocol
:
int i, j, k; // ... calculation that assigns i, j, k PyBuffer a = obj.getBuffer(PyBUF.FULL); int itemsize = a.getItemsize(); PyBuffer.Pointer b = a.getPointer(i,j,k);the item with index
[i,j,k]
is in the array b.storage
at index
[b.offset]
to [b.offset + itemsize - 1]
inclusive. And if
itemsize==1
, the item is simply the byte b.storage[b.offset]
Essentially this is a method for computing the offset of a particular index. The client is
free to navigate the underlying buffer b.storage
without respecting these
boundaries. If the buffer is non-contiguous, the above description is still valid (since a
multi-byte item must itself be contiguously stored), but in any additional navigation of
b.storage[]
to other units, the client must use the shape, strides and
sub-offsets provided by the API. Normally one starts b = a.getBuf()
in order to
establish the offset of index [0,...,0].
getPointer
in interface PyBuffer
indices
- multidimensional index at which to position the pointerIndexOutOfBoundsException
public int[] getStrides()
PyBUF
strides
array gives the distance in the storage array between adjacent items
(in each dimension). In the rawest parts of the buffer API, the consumer of the buffer is
able to navigate the exported storage. The "strides" array is part of the support for
interpreting the buffer as an n-dimensional array of items. It provides the coefficients of
the "addressing polynomial". (More on this in the CPython documentation.) The consumer must
not modify this array. A valid strides
array is always returned (difference from
CPython).getStrides
in interface PyBUF
public int[] getSuboffsets()
PyBUF
suboffsets
array is a further part of the support for interpreting the
buffer as an n-dimensional array of items, where the array potentially uses indirect
addressing (like a real Java array of arrays, in fact). This is only applicable when there is
more than 1 dimension, and it works in conjunction with the strides
array. (More
on this in the CPython documentation.) When used, suboffsets[k]
is an integer
index, not a byte offset as in CPython. The consumer must not modify this array. When not
needed for navigation null
is returned (as in CPython).getSuboffsets
in interface PyBUF
null
if not necessary for navigationpublic boolean isContiguous(char order)
PyBUF
isContiguous
in interface PyBUF
order
- 'C', 'F' or 'A', as the storage order is C, Fortran or either.public String getFormat()
PyBuffer
PyBUF.FORMAT
bit in the consumer's call to
getBuffer
, a valid format
string is always returned (difference
from CPython).
Jython only implements "B" so far, and it is debatable whether anything fancier than "<n>B" can be supported in Java.
public int getItemsize()
PyBUF
getItemsize
in interface PyBUF