Package uk.ac.starlink.array

# Interface NDArray

All Superinterfaces:
  `ArrayDescription`

All Known Implementing Classes:
  `BridgeNDArray`, `CopyNDArray`, `DummyNDArray`, `ScratchNDArray`

public interface NDArray extends ArrayDescription
N-dimensional array access. An NDArray represents a hyper-cuboidal array of java primitive values. This array is potentially large, in that it may contain more than Integer.MAX_VALUE (2^31) elements, and so its data cannot in general be assumed to fit in a java array.

This interface defines the methods used to enquire information about an NDArray and to acquire access to its pixels for read and/or write.

The bulk data of an NDArray can be considered as a vector of values of a given primitive type each labelled with a scalar offset (starting at zero), or as an N-dimensional hyper-rectangle of such primitive values each labelled with an N-element coordinate vector. The mapping between the offsets and the coordinates is determined by an `OrderedNDShape` object belonging to the NDArray.

To obtain acess to the pixel data itself it is necessary to obtain an ArrayAccess object using the `getAccess()` method. This provides methods to move around within the NDArray and read/write arrays full of pixels. It is possible to access data a pixel at a time in this way, but this is generally not very efficient, and it is better to read/write a block of pixels to/from an array of moderate size. Since there may be a large number of pixels (perhaps >2**31) in an NDArray it is not in general possible to read/write all the pixel data to/from a single primitive array, so for general processing it is necessary to step through the pixel data processing chunks at a time. The `ChunkStepper` utility class is provided as a convenience for this sort of processing. Here is a code snippet which uses this technique to calculate statistics on all the pixels in an NDArray:

         // Obtain the NDArray from somewhere.
         NDArray nda = ...

         // Get a reader object to access the pixel data.
         ArrayAccess reader = nda.getAccess();

         // Get a ChunkStepper to assist in working through the data.
         long npix = nda.getShape().getNumPixels();
         ChunkStepper chunkIt = new ChunkStepper( npix );

         // Create a primitive buffer array of the right type for the NDArray,
         // long enough to hold the biggest chunk.
         Object buffer = nda.getType().newArray( chunkIt.getSize() );

         // Step through the array in chunks, reading then processing a
         // buffer-full each time.
         for ( chunkIt.hasNext(); chunkIt.next() ) {
             int size = chunkIt.getSize();
             reader.read( buffer, 0, size );
             accumulateStatistics( buffer, 0, size );
         }

         // Relinquish the reader to allow resources to be reclaimed.
         reader.close();

In the above the `Type.newArray(int)` method is used to obtain a buffer array of the same primitive type as the NDArray - this makes it easier to write code which can process multiple numeric types without having to write special cases for each numeric type. However, if the NDArray was known to have a type of Type.FLOAT say, this line could be replaced by

         float[] buffer = new float[ chunkIt.getSize() ];

Read and write access are not always available; use the `ArrayDescription.isReadable()` and `ArrayDescription.isWritable()` methods to determine read/writablity.

While all NDArrays support one-time sequential access to the pixels from start to finish, some support other access modes.

random access
  If the `ArrayDescription.isRandom()` method returns true, then it is possible to use the `ArrayAccess.setPosition(long[])` or `ArrayAccess.setOffset(long)` methods of the reader/writer to move backwards within the array as well as forwards between accesses.
multiple accessors
  If `multipleAccess()` returns true, it is possible to call the getAccess method more than once for independent accesses to the data. This will always be the case if isRandom is true.
mapped access
  If the `ArrayAccess.isMapped()` method of the obtained ArrayAccess object returns true, the NDArray provides mapped access. This means that the `ArrayAccess.getMapped()` method will return a java primitive array containing the entire content of the NDArray

If mapped access exists, it will be the most efficent way of accessing the data. The following code calculates statistics as above in the most efficent way it can, by using mapped access if available.

         ArrayAccess reader = nda.getAccess();
         if ( reader.isMapped() ) {
             accumulateStatistics( reader.getMapped() );
         }
         else {
             ... step through in chunks as previous example
         }

An NDArray is normally created using the `NDArrayFactory` class. This class can also be used to obtain an NDArray with random access from one which doesn't have it.

Author:
  Mark Taylor (Starlink)

## Method Summary

Modifier and Type
Method
Description
`void`
`close()`
Declares that this NDArray will not be required for further use; in particular that no further invocations will be made of the getAccess method.
`ArrayAccess`
`getAccess()`
Returns an object which can read and/or write the pixels of this NDArray.
`URL`
`getURL()`
Returns the URL of this NDArray, or null if it does not have one.
`boolean`
`multipleAccess()`
Indicates whether multiple calls to the getAccess method may be made.

### Methods inherited from interface uk.ac.starlink.array.ArrayDescription

`getBadHandler, getShape, getType, isRandom, isReadable, isWritable`

## Method Details

### getURL

URL getURL()
Returns the URL of this NDArray, or null if it does not have one. An NDArray will normally only have a URL if it has been created using one of the calls to NDArrayFactory which takes a URL argument. Feeding the returned URL to the NDArrayFactory will obtain the same NDArray as this one. Scratch NDArrays, and virtual NDArrays created by wrapping another to give access using different characteristics (type, shape etc) will in general not have a URL. The return value must not change over the lifetime of this object.

Returns:
  the URL at which this NDArray resides, or null if it is not persistent

### multipleAccess

boolean multipleAccess()
Indicates whether multiple calls to the getAccess method may be made. This method will always return true if the `ArrayDescription.isRandom()` method returns true, but may do so even for non-random arrays.

Returns:
  true if multiple independent accessor objects are available

### getAccess

ArrayAccess getAccess() throws IOException
Returns an object which can read and/or write the pixels of this NDArray. The returned ArrayReader should be closed when it is no longer required; this enables resources it may hold to be released.

Each call to this method returns a new and independent ArrayAccess object. However it may or may not be possible to call it more than once; the `multipleAccess()` method indicates whether this is the case.

Returns:
  an accessor for the pixel data
Throws:
  `IOException` - if there is some I/O error
  `IllegalStateException` - if close has been called on this NDArray
  `UnsupportedOperationException` - if multipleAccess returns false and getAccess has already been invoked once

### close

void close() throws IOException
Declares that this NDArray will not be required for further use; in particular that no further invocations will be made of the getAccess method.

This method should be invoked on an NDArray when it is no longer required for pixel access. This allows reclamation of non-memory resources, and in the case of writable arrays it may also be required to ensure that data is flushed from buffers back to the actual pixel array.

An array should not however be closed if some other object might still require pixel access to it via a retained reference. The general rule is that an application which obtains a new NDArray from a URL should arrange for `close` to be called on it at the end of its lifetime, but that utility routines which perform operations on an NDArray should not close it after use (though they should close any `ArrayAccess` objects which they take out). Note that closing an NDArray will normally result in closing any arrays which it wraps.

Multiple calls of this method may harmlessly be made.

Throws:
  `IOException` - if some I/O error occurred