This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 64f25a50662685b394825cd4bd40215714bfb456
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Feb 13 10:53:14 2025 +0100

    Allow `HyperRectangleReader` to store the values in an existing array 
instead of allocating a new one.
---
 .../org/apache/sis/image/privy/ImageUtilities.java |   8 +-
 .../apache/sis/image/privy/ImageUtilitiesTest.java |  16 +-
 .../org/apache/sis/storage/geotiff/DataSubset.java |   2 +-
 .../org/apache/sis/io/stream/ChannelDataInput.java | 174 ++++++++++-----------
 .../org/apache/sis/io/stream/DataTransfer.java     |  28 ++--
 .../apache/sis/io/stream/HyperRectangleReader.java |  28 +++-
 .../apache/sis/io/stream/MemoryDataTransfer.java   |  22 +--
 .../main/org/apache/sis/io/stream/Region.java      |  13 +-
 .../apache/sis/storage/esri/RawRasterReader.java   |  20 ++-
 .../apache/sis/storage/esri/RawRasterStore.java    |  16 +-
 .../sis/io/stream/HyperRectangleReaderTest.java    |  10 +-
 11 files changed, 190 insertions(+), 147 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
index 18f6eeea7b..01f0325d61 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ImageUtilities.java
@@ -349,7 +349,7 @@ public final class ImageUtilities extends Static {
     }
 
     /**
-     * The values to be returned by {@link #toNumberEnum(int)}.
+     * The values to be returned by {@link #toNumberEnum(DataType)}.
      */
     private static final byte[] NUMBER_ENUMS = {
         Numbers.BYTE, Numbers.SHORT, Numbers.SHORT, Numbers.INTEGER, 
Numbers.FLOAT, Numbers.DOUBLE
@@ -359,11 +359,11 @@ public final class ImageUtilities extends Static {
      * Converts a {@link DataBuffer} enumeration value to {@link Numbers} 
enumeration value.
      * This method ignores whether the type is signed or unsigned.
      *
-     * @param  dataType  the {@link DataBuffer} enumeration value.
+     * @param  type  the {@link DataBuffer} enumeration value.
      * @return the {@link Numbers} enumeration value.
      */
-    public static byte toNumberEnum(final int dataType) {
-        return (dataType >= 0 && dataType < NUMBER_ENUMS.length) ? 
NUMBER_ENUMS[dataType] : Numbers.OTHER;
+    public static byte toNumberEnum(final DataType type) {
+        return NUMBER_ENUMS[type.toDataBufferType()];
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
index b8b00c3b7b..17b6973679 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/image/privy/ImageUtilitiesTest.java
@@ -24,6 +24,7 @@ import java.awt.image.RenderedImage;
 import java.awt.image.BufferedImage;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
+import org.apache.sis.image.DataType;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Vocabulary;
 import static org.apache.sis.util.privy.Numerics.COMPARISON_THRESHOLD;
@@ -176,17 +177,16 @@ public final class ImageUtilitiesTest extends TestCase {
     }
 
     /**
-     * Tests {@link ImageUtilities#toNumberEnum(int)}.
+     * Tests {@link ImageUtilities#toNumberEnum(DataType)}.
      */
     @Test
     public void testToNumberEnum() {
-        assertEquals(Numbers.BYTE,    
ImageUtilities.toNumberEnum(DataBuffer.TYPE_BYTE));
-        assertEquals(Numbers.SHORT,   
ImageUtilities.toNumberEnum(DataBuffer.TYPE_SHORT));
-        assertEquals(Numbers.SHORT,   
ImageUtilities.toNumberEnum(DataBuffer.TYPE_USHORT));
-        assertEquals(Numbers.INTEGER, 
ImageUtilities.toNumberEnum(DataBuffer.TYPE_INT));
-        assertEquals(Numbers.FLOAT,   
ImageUtilities.toNumberEnum(DataBuffer.TYPE_FLOAT));
-        assertEquals(Numbers.DOUBLE,  
ImageUtilities.toNumberEnum(DataBuffer.TYPE_DOUBLE));
-        assertEquals(Numbers.OTHER,   
ImageUtilities.toNumberEnum(DataBuffer.TYPE_UNDEFINED));
+        assertEquals(Numbers.BYTE,    
ImageUtilities.toNumberEnum(DataType.BYTE));
+        assertEquals(Numbers.SHORT,   
ImageUtilities.toNumberEnum(DataType.SHORT));
+        assertEquals(Numbers.SHORT,   
ImageUtilities.toNumberEnum(DataType.USHORT));
+        assertEquals(Numbers.INTEGER, 
ImageUtilities.toNumberEnum(DataType.INT));
+        assertEquals(Numbers.FLOAT,   
ImageUtilities.toNumberEnum(DataType.FLOAT));
+        assertEquals(Numbers.DOUBLE,  
ImageUtilities.toNumberEnum(DataType.DOUBLE));
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
index baab4b312c..2a30e3dfda 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
@@ -519,7 +519,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
          * If that assumption was not true, we would have to adjust 
`capacity`, `lower[0]` and `upper[0]`
          * (we may do that as an optimization in a future version).
          */
-        final var hr     = new 
HyperRectangleReader(ImageUtilities.toNumberEnum(type.toDataBufferType()), 
input());
+        final var hr     = new 
HyperRectangleReader(ImageUtilities.toNumberEnum(type), input());
         final var region = new Region(size, lower, upper, subsampling);
         final var banks  = new Buffer[numBanks];
         for (int b=0; b<numBanks; b++) {
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
index 61b30d9567..8616626267 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelDataInput.java
@@ -647,7 +647,7 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      * Helper class for the {@code readFully(…)} methods,
      * in order to avoid duplicating almost identical code many times.
      */
-    abstract class ArrayReader implements DataTransfer {
+    abstract class ArrayReader extends DataTransfer {
         /**
          * For subclass constructors only.
          */
@@ -658,7 +658,7 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
          * Returns a file identifier for error messages or debugging purpose.
          */
         @Override
-        public final String filename() {
+        final String filename() {
             return filename;
         }
 
@@ -681,7 +681,7 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
          * Moves to the given position in the stream.
          */
         @Override
-        public final void seek(long n) throws IOException {
+        final void seek(long n) throws IOException {
             ChannelDataInput.this.seek(n);
         }
 
@@ -699,7 +699,7 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
          * @throws IOException if an error (including EOF) occurred while 
reading the stream.
          */
         @Override
-        public void readFully(Buffer view, int offset, int length) throws 
IOException {
+        void readFully(Buffer view, int offset, int length) throws IOException 
{
             final int dataSizeShift = dataSizeShift();
             ensureBufferContains(Math.min(length << dataSizeShift, 
buffer.capacity()));
             if (view == null) {
@@ -735,17 +735,17 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class BytesReader extends ArrayReader {
-        /** The array where to store the values. */      private byte[] dest;
-        BytesReader(final byte[] dest)                  {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 0;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
ByteBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return buffer;}
-        @Override public Buffer createView()            {return buffer;}
-        @Override public void   createDataArray(int n)  {dest = new byte[n];}
-        @Override        void   transfer(int p, int n)  {buffer.get(dest, p, 
n);}
-        @Override public void   setDest(Object array)   {dest = (byte[]) 
array;};
-        @Override public void readFully(Buffer view, int offset, int length) 
throws IOException {
+        /** The array where to store the values. */ private byte[] dest;
+        BytesReader(final byte[] dest)           {this.dest = dest;}
+        @Override int    dataSizeShift()         {return 0;}
+        @Override Object dataArray()             {return dest;}
+        @Override Buffer dataArrayAsBuffer()     {return 
ByteBuffer.wrap(dest);}
+        @Override Buffer view()                  {return buffer;}
+        @Override Buffer createView()            {return buffer;}
+        @Override void   createDataArray(int n)  {dest = new byte[n];}
+        @Override void   transfer(int p, int n)  {buffer.get(dest, p, n);}
+        @Override void   setDest(Object array)   {dest = (byte[]) array;};
+        @Override void readFully(Buffer view, int offset, int length) throws 
IOException {
             ChannelDataInput.this.readFully(dest, offset, length);
         }
     };
@@ -755,18 +755,18 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class CharsReader extends ArrayReader {
-        /** A view over the enclosing byte buffer. */    private CharBuffer 
view;
-        /** The array where to store the values.   */    private char[] dest;
-        CharsReader(final CharBuffer source)            {this.view = source;}
-        CharsReader(final char[] dest)                  {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 1;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
CharBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return view;}
-        @Override public Buffer createView()            {return view = 
buffer.asCharBuffer();}
-        @Override public void   createDataArray(int n)  {dest = new char[n];}
-        @Override        void   transfer(int p, int n)  {view.get(dest, p, n);}
-        @Override public void   setDest(Object array)   {dest = (char[]) 
array;};
+        /** A view over the enclosing byte buffer. */ private CharBuffer view;
+        /** The array where to store the values.   */ private char[] dest;
+        CharsReader(final CharBuffer source)     {this.view = source;}
+        CharsReader(final char[] dest)           {this.dest = dest;}
+        @Override int    dataSizeShift()         {return 1;}
+        @Override Object dataArray()             {return dest;}
+        @Override Buffer dataArrayAsBuffer()     {return 
CharBuffer.wrap(dest);}
+        @Override Buffer view()                  {return view;}
+        @Override Buffer createView()            {return view = 
buffer.asCharBuffer();}
+        @Override void   createDataArray(int n)  {dest = new char[n];}
+        @Override void   transfer(int p, int n)  {view.get(dest, p, n);}
+        @Override void   setDest(Object array)   {dest = (char[]) array;};
     };
 
     /**
@@ -774,18 +774,18 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class ShortsReader extends ArrayReader {
-        /** A view over the enclosing byte buffer. */    private ShortBuffer 
view;
-        /** The array where to store the values.   */    private short[] dest;
-        ShortsReader(final ShortBuffer source)          {this.view = source;}
-        ShortsReader(final short[] dest)                {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 1;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
ShortBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return view;}
-        @Override public Buffer createView()            {return view = 
buffer.asShortBuffer();}
-        @Override public void   createDataArray(int n)  {dest = new short[n];}
-        @Override void          transfer(int p, int n)  {view.get(dest, p, n);}
-        @Override public void   setDest(Object array)   {dest = (short[]) 
array;};
+        /** A view over the enclosing byte buffer. */ private ShortBuffer view;
+        /** The array where to store the values.   */ private short[] dest;
+        ShortsReader(final ShortBuffer source)   {this.view = source;}
+        ShortsReader(final short[] dest)         {this.dest = dest;}
+        @Override int    dataSizeShift()         {return 1;}
+        @Override Object dataArray()             {return dest;}
+        @Override Buffer dataArrayAsBuffer()     {return 
ShortBuffer.wrap(dest);}
+        @Override Buffer view()                  {return view;}
+        @Override Buffer createView()            {return view = 
buffer.asShortBuffer();}
+        @Override void   createDataArray(int n)  {dest = new short[n];}
+        @Override void   transfer(int p, int n)  {view.get(dest, p, n);}
+        @Override void   setDest(Object array)   {dest = (short[]) array;};
     };
 
     /**
@@ -793,18 +793,18 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class IntsReader extends ArrayReader {
-        /** A view over the enclosing byte buffer. */    private IntBuffer 
view;
-        /** The array where to store the values.   */    private int[] dest;
-        IntsReader(final IntBuffer source)              {this.view = source;}
-        IntsReader(final int[] dest)                    {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 2;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
IntBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return view;}
-        @Override public Buffer createView()            {return view = 
buffer.asIntBuffer();}
-        @Override public void   createDataArray(int n)  {dest = new int[n];}
-        @Override void          transfer(int p, int n)  {view.get(dest, p, n);}
-        @Override public void   setDest(Object array)   {dest = (int[]) 
array;};
+        /** A view over the enclosing byte buffer. */ private IntBuffer view;
+        /** The array where to store the values.   */ private int[] dest;
+        IntsReader(final IntBuffer source)        {this.view = source;}
+        IntsReader(final int[] dest)              {this.dest = dest;}
+        @Override int    dataSizeShift()          {return 2;}
+        @Override Object dataArray()              {return dest;}
+        @Override Buffer dataArrayAsBuffer()      {return 
IntBuffer.wrap(dest);}
+        @Override Buffer view()                   {return view;}
+        @Override Buffer createView()             {return view = 
buffer.asIntBuffer();}
+        @Override void   createDataArray(int n)   {dest = new int[n];}
+        @Override void   transfer(int p, int n)   {view.get(dest, p, n);}
+        @Override void   setDest(Object array)    {dest = (int[]) array;};
     };
 
     /**
@@ -812,18 +812,18 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class LongsReader extends ArrayReader {
-        /** A view over the enclosing byte buffer. */    private LongBuffer 
view;
-        /** The array where to store the values.   */    private long[] dest;
-        LongsReader(final LongBuffer source)            {this.view = source;}
-        LongsReader(final long[] dest)                  {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 3;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
LongBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return view;}
-        @Override public Buffer createView()            {return view = 
buffer.asLongBuffer();}
-        @Override public void   createDataArray(int n)  {dest = new long[n];}
-        @Override void          transfer(int p, int n)  {view.get(dest, p, n);}
-        @Override public void   setDest(Object array)   {dest = (long[]) 
array;};
+        /** A view over the enclosing byte buffer. */ private LongBuffer view;
+        /** The array where to store the values.   */ private long[] dest;
+        LongsReader(final LongBuffer source)     {this.view = source;}
+        LongsReader(final long[] dest)           {this.dest = dest;}
+        @Override int    dataSizeShift()         {return 3;}
+        @Override Object dataArray()             {return dest;}
+        @Override Buffer dataArrayAsBuffer()     {return 
LongBuffer.wrap(dest);}
+        @Override Buffer view()                  {return view;}
+        @Override Buffer createView()            {return view = 
buffer.asLongBuffer();}
+        @Override void   createDataArray(int n)  {dest = new long[n];}
+        @Override void   transfer(int p, int n)  {view.get(dest, p, n);}
+        @Override void   setDest(Object array)   {dest = (long[]) array;};
     };
 
     /**
@@ -831,18 +831,18 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class FloatsReader extends ArrayReader {
-        /** A view over the enclosing byte buffer. */    private FloatBuffer 
view;
-        /** The array where to store the values.   */    private float[] dest;
-        FloatsReader(final FloatBuffer source)          {this.view = source;}
-        FloatsReader(final float[] dest)                {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 2;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
FloatBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return view;}
-        @Override public Buffer createView()            {return view = 
buffer.asFloatBuffer();}
-        @Override public void   createDataArray(int n)  {dest = new float[n];}
-        @Override void          transfer(int p, int n)  {view.get(dest, p, n);}
-        @Override public void   setDest(Object array)   {dest = (float[]) 
array;};
+        /** A view over the enclosing byte buffer. */ private FloatBuffer view;
+        /** The array where to store the values.   */ private float[] dest;
+        FloatsReader(final FloatBuffer source)   {this.view = source;}
+        FloatsReader(final float[] dest)         {this.dest = dest;}
+        @Override int    dataSizeShift()         {return 2;}
+        @Override Object dataArray()             {return dest;}
+        @Override Buffer dataArrayAsBuffer()     {return 
FloatBuffer.wrap(dest);}
+        @Override Buffer view()                  {return view;}
+        @Override Buffer createView()            {return view = 
buffer.asFloatBuffer();}
+        @Override void   createDataArray(int n)  {dest = new float[n];}
+        @Override void   transfer(int p, int n)  {view.get(dest, p, n);}
+        @Override void   setDest(Object array)   {dest = (float[]) array;};
     };
 
     /**
@@ -850,18 +850,18 @@ public class ChannelDataInput extends ChannelData 
implements DataInput {
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     final class DoublesReader extends ArrayReader {
-        /** A view over the enclosing byte buffer. */    private DoubleBuffer 
view;
-        /** The array where to store the values.   */    private double[] dest;
-        DoublesReader(final DoubleBuffer source)        {this.view = source;}
-        DoublesReader(final double[] dest)              {this.dest = dest;}
-        @Override public int    dataSizeShift()         {return 3;}
-        @Override public Object dataArray()             {return dest;}
-        @Override public Buffer dataArrayAsBuffer()     {return 
DoubleBuffer.wrap(dest);}
-        @Override public Buffer view()                  {return view;}
-        @Override public Buffer createView()            {return view = 
buffer.asDoubleBuffer();}
-        @Override public void   createDataArray(int n)  {dest = new double[n];}
-        @Override void          transfer(int p, int n)  {view.get(dest, p, n);}
-        @Override public void   setDest(Object array)   {dest = (double[]) 
array;};
+        /** A view over the enclosing byte buffer. */ private DoubleBuffer 
view;
+        /** The array where to store the values.   */ private double[] dest;
+        DoublesReader(final DoubleBuffer source) {this.view = source;}
+        DoublesReader(final double[] dest)       {this.dest = dest;}
+        @Override int    dataSizeShift()         {return 3;}
+        @Override Object dataArray()             {return dest;}
+        @Override Buffer dataArrayAsBuffer()     {return 
DoubleBuffer.wrap(dest);}
+        @Override Buffer view()                  {return view;}
+        @Override Buffer createView()            {return view = 
buffer.asDoubleBuffer();}
+        @Override void   createDataArray(int n)  {dest = new double[n];}
+        @Override void   transfer(int p, int n)  {view.get(dest, p, n);}
+        @Override void   setDest(Object array)   {dest = (double[]) array;};
     };
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/DataTransfer.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/DataTransfer.java
index 0bae94a8b7..124218eb2a 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/DataTransfer.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/DataTransfer.java
@@ -26,11 +26,17 @@ import java.nio.Buffer;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-interface DataTransfer {
+abstract class DataTransfer {
+    /**
+     * Creates a new instance.
+     */
+    DataTransfer() {
+    }
+
     /**
      * Returns a file identifier for error messages or debugging purpose.
      */
-    String filename();
+    abstract String filename();
 
     /**
      * Returns the size of the Java primitive type which is the element of the 
array.
@@ -40,41 +46,41 @@ interface DataTransfer {
      *     dataSize = 1 << dataSizeShift;
      * }
      */
-    int dataSizeShift();
+    abstract int dataSizeShift();
 
     /**
      * Returns the data as a {@code char[]}, {@code short[]}, {@code int[]}, 
{@code long[]},
      * {@code float[]} or {@code double[]} array. This is either the array 
given in argument
      * to the subclass constructor, or the array created by {@link 
#createDataArray(int)}.
      */
-    Object dataArray();
+    abstract Object dataArray();
 
     /**
      * Returns {@link #dataArray()} wrapped in a buffer.
      */
-    Buffer dataArrayAsBuffer();
+    abstract Buffer dataArrayAsBuffer();
 
     /**
      * Creates a destination array of the given length.
      */
-    void createDataArray(int length);
+    abstract void createDataArray(int length);
 
     /**
      * Sets the destination to the given data array, which may be {@code null}.
      */
-    void setDest(Object array) throws ClassCastException;
+    abstract void setDest(Object array) throws ClassCastException;
 
     /**
      * Returns the view created by the last call to {@link #createView()}, or 
{@code null} if none.
      */
-    Buffer view();
+    abstract Buffer view();
 
     /**
      * Creates a new buffer of the type required by the array to fill.
      * This method is guaranteed to be invoked exactly once, after the
      * {@link ChannelDataInput#buffer} contains enough data.
      */
-    Buffer createView();
+    abstract Buffer createView();
 
     /**
      * Moves to the given position in the stream.
@@ -82,7 +88,7 @@ interface DataTransfer {
      * @param  position  the position where to move.
      * @throws IOException if the stream cannot be moved to the given position.
      */
-    void seek(long position) throws IOException;
+    abstract void seek(long position) throws IOException;
 
     /**
      * Reads {@code length} values from the stream and stores them into the 
array known to subclass,
@@ -97,5 +103,5 @@ interface DataTransfer {
      * @param  length   the number of values to read.
      * @throws IOException if an error (including EOF) occurred while reading 
the stream.
      */
-    void readFully(Buffer view, int offset, int length) throws IOException;
+    abstract void readFully(Buffer view, int offset, int length) throws 
IOException;
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleReader.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleReader.java
index 5d7274adab..9059649563 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleReader.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleReader.java
@@ -66,7 +66,7 @@ public class HyperRectangleReader {
      */
     public HyperRectangleReader(final byte dataType, final ChannelDataInput 
input) throws DataStoreContentException {
         switch (dataType) {
-            case Numbers.BYTE:      reader = input.new BytesReader  (          
 null); break;
+            case Numbers.BYTE:      reader = input.new BytesReader  ((byte[])  
 null); break;
             case Numbers.CHARACTER: reader = input.new CharsReader  ((char[])  
 null); break;
             case Numbers.SHORT:     reader = input.new ShortsReader ((short[]) 
 null); break;
             case Numbers.INTEGER:   reader = input.new IntsReader   ((int[])   
 null); break;
@@ -135,6 +135,26 @@ public class HyperRectangleReader {
         origin = p;
     }
 
+    /**
+     * Sets the destination where values will be stored.
+     * It is caller's responsibility to ensure that the buffer has sufficient 
capacity.
+     * If this method is not invoked, the destination array will be created 
automatically.
+     *
+     * <h4>Limitations</h4>
+     * The current implementation accepts only buffer wrapping Java array 
starting at zero.
+     * The buffer limit is ignored. Those limitations may be resolved in a 
future version.
+     *
+     * @param  dest  buffer wrapping the array where values will be stored.
+     * @throws UnsupportedOperationException if the buffer is not backed by an 
accessible array or does not start at 0.
+     * @throws ClassCastException if {@code array} is an array of the 
primitive type identified by {@code dataType}.
+     */
+    public final void setDestination(final Buffer dest) {
+        if ((dest.arrayOffset() | dest.position()) != 0) {
+            throw new UnsupportedOperationException();
+        }
+        reader.setDest(dest.array());
+    }
+
     /**
      * Reads data in the given region. It is caller's responsibility to ensure 
that the {@code Region}
      * object has been created with a {@code size} argument equals to this 
hyper-rectangle size.
@@ -179,7 +199,7 @@ public class HyperRectangleReader {
         final long[] strides = new long[region.getDimension() - 
contiguousDataDimension];
         final int[]   cursor = new int[strides.length];
         final int sampleSize = sampleSize();
-        long  streamPosition = Math.addExact(origin, 
region.offset(sampleSize));
+        long  streamPosition = Math.addExact(origin, 
region.getStartByteOffset(sampleSize));
         int    arrayPosition = 0;
         for (int i=0; i<strides.length; i++) {
             strides[i] = region.stride(i + contiguousDataDimension, 
contiguousDataLength, sampleSize);
@@ -187,7 +207,9 @@ public class HyperRectangleReader {
         }
         final int limit = region.targetLength(region.getDimension());
         try {
-            reader.createDataArray(Math.max(capacity, limit));
+            if (reader.dataArray() == null) {
+                reader.createDataArray(Math.max(capacity, limit));
+            }
             final Buffer view = reader.view();
 loop:       do {
                 reader.seek(streamPosition);
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/MemoryDataTransfer.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/MemoryDataTransfer.java
index d6c1cd3ed9..e78ce3b260 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/MemoryDataTransfer.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/MemoryDataTransfer.java
@@ -42,7 +42,7 @@ import org.apache.sis.util.resources.Errors;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-final class MemoryDataTransfer implements DataTransfer, ReadableByteChannel {
+final class MemoryDataTransfer extends DataTransfer implements 
ReadableByteChannel {
     /**
      * The actual {@code DataTransfer} implementation.
      */
@@ -75,7 +75,7 @@ final class MemoryDataTransfer implements DataTransfer, 
ReadableByteChannel {
      * Moves to the given byte position in the buffer.
      */
     @Override
-    public void seek(long position) throws IOException {
+    void seek(long position) throws IOException {
         final int dataSizeShift = dataSizeShift();
         if (position < 0 || (position & ((1 << dataSizeShift) - 1)) != 0) {
             throw new 
IOException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "position", 
position));
@@ -91,14 +91,14 @@ final class MemoryDataTransfer implements DataTransfer, 
ReadableByteChannel {
     /**
      * Delegates to the actual implementation.
      */
-    @Override public String filename()                  {return 
reader.filename();}
-    @Override public int    dataSizeShift()             {return 
reader.dataSizeShift();}
-    @Override public Object dataArray()                 {return 
reader.dataArray();}
-    @Override public Buffer dataArrayAsBuffer()         {return 
reader.dataArrayAsBuffer();}
-    @Override public Buffer view()                      {return reader.view();}
-    @Override public Buffer createView()                {return 
reader.createView();}
-    @Override public void   createDataArray(int length) 
{reader.createDataArray(length);}
-    @Override public void   setDest(Object array)       
{reader.setDest(array);}
+    @Override String filename()                  {return reader.filename();}
+    @Override int    dataSizeShift()             {return 
reader.dataSizeShift();}
+    @Override Object dataArray()                 {return reader.dataArray();}
+    @Override Buffer dataArrayAsBuffer()         {return 
reader.dataArrayAsBuffer();}
+    @Override Buffer view()                      {return reader.view();}
+    @Override Buffer createView()                {return reader.createView();}
+    @Override void   createDataArray(int length) 
{reader.createDataArray(length);}
+    @Override void   setDest(Object array)       {reader.setDest(array);}
 
     /**
      * Reads {@code length} values from the buffer and stores them into the 
array known to subclass,
@@ -109,7 +109,7 @@ final class MemoryDataTransfer implements DataTransfer, 
ReadableByteChannel {
      * @param  length  the number of values to read.
      */
     @Override
-    public void readFully(final Buffer view, final int offset, final int 
length) {
+    void readFully(final Buffer view, final int offset, final int length) {
         reader.transfer(offset, length);
     }
 
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
index db2039b775..a7cc2bd32a 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
@@ -55,6 +55,8 @@ public final class Region {
     /**
      * Position of the first value to read.
      * This position is zero if the value of all {@code regionLower} elements 
is zero.
+     *
+     * @see #getStartByteOffset(long)
      */
     final long startAt;
 
@@ -78,6 +80,8 @@ public final class Region {
      * Additional values to add to {@link #skips}, but in bytes instead of as 
a number of values.
      * This is the only field in this {@link Region} class to be expressed in 
byte units.
      * This offset is rarely provided.
+     *
+     * @see #setAdditionalByteOffset(int, long)
      */
     private long[] skipBytes;
 
@@ -193,10 +197,12 @@ public final class Region {
      * Returns the offset in bytes where reading should start.
      * Offset is relative to the first sample value of the hyper-cube.
      *
-     * @param  sampleSize   size of sample values, in bytes.
+     * @param  sampleSize   size of sample values, in bytes. The value is 
usually between {@value Byte#BYTES} and
+     *         {@value Long#BYTES}, but the type is nevertheless a {@code 
long} for implementation convenience.
      * @return offset in bytes relative to the first sample value.
+     * @throws ArithmeticException if the offset overflows the 64-bits integer 
capacity.
      */
-    final long offset(long sampleSize) {
+    public final long getStartByteOffset(long sampleSize) {
         if (skipBytes != null) {
             // This additional offset is in bytes.
             sampleSize = addExact(sampleSize, skipBytes[0]);
@@ -248,7 +254,8 @@ public final class Region {
     }
 
     /**
-     * Returns the size after reading only the sub-region at the given 
subsampling in the given dimension.
+     * Returns the size after reading only the sub-region in the given 
dimension.
+     * This size takes in account the sub-sampling specified at construction 
time.
      *
      * @param  dimension  the dimension for which to get the target size.
      * @return expected number of elements in the given dimension.
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
index 987c775f89..2ce3532735 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
@@ -72,6 +72,12 @@ final class RawRasterReader extends HyperRectangleReader {
      */
     final GridGeometry gridGeometry;
 
+    /**
+     * The enumeration value that represents the type of sample values.
+     * Shall be consistent with {@code layout.getDataType()}.
+     */
+    private final DataType dataType;
+
     /**
      * Image layout, which describes also the layout of data to read.
      */
@@ -95,16 +101,18 @@ final class RawRasterReader extends HyperRectangleReader {
      * Creates a new reader for the given input.
      *
      * @param  gridGeometry  the full image size together with the "grid to 
CRS" transform.
+     * @param  dataType      the type of sample value. Shall be consistent 
with {@code layout}.
      * @param  layout        the image layout, which describes also the layout 
of data to read.
      * @param  bandGapBytes  Number of bytes to skip between band. Used with 
{@link BandedSampleModel} only.
      * @param  input         the channel from which to read the values, 
together with a buffer for transferring data.
      * @throws DataStoreContentException if the given {@code dataType} is not 
one of the supported values.
      */
-    public RawRasterReader(final GridGeometry gridGeometry, final SampleModel 
layout, final int bandGapBytes,
-            final ChannelDataInput input) throws DataStoreContentException
+    public RawRasterReader(final GridGeometry gridGeometry, final DataType 
dataType, final SampleModel layout,
+            final int bandGapBytes, final ChannelDataInput input) throws 
DataStoreContentException
     {
-        super(ImageUtilities.toNumberEnum(layout.getDataType()), input);
+        super(ImageUtilities.toNumberEnum(dataType), input);
         this.gridGeometry = gridGeometry;
+        this.dataType     = dataType;
         this.layout       = layout;
         this.bandGapBytes = bandGapBytes;
     }
@@ -198,8 +206,8 @@ final class RawRasterReader extends HyperRectangleReader {
         final Buffer[] buffer;
         SampleModel sm = layout;
         boolean bandSubsetApplied = range.isIdentity();
-        if (layout instanceof BandedSampleModel) {
-            final BandedSampleModel cm = (BandedSampleModel) layout;
+        if (sm instanceof BandedSampleModel) {
+            final var cm = (BandedSampleModel) sm;
             if (!(ArraysExt.allEquals(cm.getBandOffsets(), 0)) && 
ArraysExt.isRange(0, cm.getBankIndices())) {
                 throw new DataStoreException("Not yet supported.");
             }
@@ -239,7 +247,7 @@ final class RawRasterReader extends HyperRectangleReader {
         if (regionWidth != width || regionHeight != height) {
             sm = sm.createCompatibleSampleModel(regionWidth, regionHeight);
         }
-        final DataBuffer data = 
RasterFactory.wrap(DataType.forDataBufferType(sm.getDataType()), buffer);
+        final DataBuffer data = RasterFactory.wrap(dataType, buffer);
         WritableRaster raster = WritableRaster.createWritableRaster(sm, data, 
null);
         if (!bandSubsetApplied) {
             raster = raster.createWritableChild(0, 0, raster.getWidth(), 
raster.getHeight(), 0, 0, range.getSelectedBands());
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterStore.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterStore.java
index 99d2d8e0d3..13be6aad5d 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterStore.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterStore.java
@@ -23,7 +23,6 @@ import java.io.IOException;
 import java.nio.ByteOrder;
 import java.nio.file.Path;
 import java.net.URISyntaxException;
-import java.awt.image.DataBuffer;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
 import java.awt.image.ComponentSampleModel;
@@ -425,8 +424,8 @@ final class RawRasterStore extends RasterStore {
             throw missingProperty(header, (nrows == 0) ? NROWS : NCOLS);
         }
         // Invoke following method now because it does argument validation.
-        final int dataType = DataType.forNumberOfBits(nbits, false, 
signed).toDataBufferType();
-        final int bytesPerSample = DataBuffer.getDataTypeSize(dataType) / 
Byte.SIZE;            // Can be zero.
+        final var dataType = DataType.forNumberOfBits(nbits, false, signed);
+        final int bytesPerSample = dataType.bytes();
         switch (geomask) {
             case 0:  ulymap = ncols - 1; break;     // No property specified.
             case 3:  break;                         // ULXMAP and ULYMAP 
specified.
@@ -454,6 +453,7 @@ final class RawRasterStore extends RasterStore {
          * block must be consistent with the expectations of `read(…)` method 
implementation.
          */
         SampleModel sampleModel = null;
+        final int bt = dataType.toDataBufferType();
         switch (layout) {
             case BIL: {
                 ignoredProperty(BANDGAPBYTES, bandGapBytes);
@@ -467,7 +467,7 @@ final class RawRasterStore extends RasterStore {
                     for (int i=1; i<nbands; i++) {
                         bandOffsets[i] = multiplyExact(bandStride, i);
                     }
-                    sampleModel = new ComponentSampleModel(dataType, ncols, 
nrows, 1, scanlineStride, bankIndices, bandOffsets);
+                    sampleModel = new ComponentSampleModel(bt, ncols, nrows, 
1, scanlineStride, bankIndices, bandOffsets);
                 }
                 break;
             }
@@ -480,7 +480,7 @@ final class RawRasterStore extends RasterStore {
                 if (bytesPerSample != 0) {
                     final int   scanlineStride = wholeDiv(totalRowBytes, 
bytesPerSample);
                     final int[] bandOffsets    = ArraysExt.range(0, nbands);
-                    sampleModel = new PixelInterleavedSampleModel(dataType, 
ncols, nrows, nbands, scanlineStride, bandOffsets);
+                    sampleModel = new PixelInterleavedSampleModel(bt, ncols, 
nrows, nbands, scanlineStride, bandOffsets);
                 }
                 break;
             }
@@ -493,7 +493,7 @@ final class RawRasterStore extends RasterStore {
                     final int   scanlineStride = wholeDiv(totalRowBytes, 
bytesPerSample);
                     final int[] bankIndices    = ArraysExt.range(0, nbands);
                     final int[] bandOffsets    = new int[nbands];
-                    sampleModel = new BandedSampleModel(dataType, ncols, 
nrows, scanlineStride, bankIndices, bandOffsets);
+                    sampleModel = new BandedSampleModel(bt, ncols, nrows, 
scanlineStride, bankIndices, bandOffsets);
                 }
                 break;
             }
@@ -503,13 +503,13 @@ final class RawRasterStore extends RasterStore {
             if (nbands != 1) {
                 throw new 
DataStoreContentException(errors().getString(Errors.Keys.InconsistentAttribute_2,
 nbits, NBITS));
             }
-            sampleModel = new MultiPixelPackedSampleModel(dataType, ncols, 
nrows, nbits, totalRowBytes, 0);
+            sampleModel = new MultiPixelPackedSampleModel(bt, ncols, nrows, 
nbits, totalRowBytes, 0);
         }
         /*
          * Prepare the reader as the last step because non-null `reader` field 
is used
          * as a sentinel value meaning that the initialization has been 
completed.
          */
-        reader = new RawRasterReader(gg, sampleModel, bandGapBytes, input);
+        reader = new RawRasterReader(gg, dataType, sampleModel, bandGapBytes, 
input);
         reader.setOrigin(skipBytes);
     }
 
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
index 60c0c8d441..79b77059a0 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
@@ -113,7 +113,7 @@ public final class HyperRectangleReaderTest extends 
TestCase {
          * different than zero.
          */
         final int origin = random.nextInt(10);
-        final byte[] array = new byte[origin + length*Short.BYTES];
+        final var array  = new byte[origin + length*Short.BYTES];
         for (int i=0; i<origin; i++) {
             array[i] = (byte) random.nextInt(0x100);
         }
@@ -133,9 +133,9 @@ public final class HyperRectangleReaderTest extends 
TestCase {
         }
         assertEquals(length, view.position());
         if (useChannel) {
-            final ByteArrayChannel channel = new ByteArrayChannel(array, true);
-            final ByteBuffer       buffer  = 
ByteBuffer.allocate(random.nextInt(20) + 20).order(ByteOrder.nativeOrder());
-            final ChannelDataInput input   = new 
ChannelDataInput("HyperRectangle in channel", channel, buffer, false);
+            final var channel = new ByteArrayChannel(array, true);
+            final var buffer  = ByteBuffer.allocate(random.nextInt(20) + 
20).order(ByteOrder.nativeOrder());
+            final var input   = new ChannelDataInput("HyperRectangle in 
channel", channel, buffer, false);
             reader = new HyperRectangleReader(Numbers.SHORT, input);
             reader.setOrigin(origin);
         } else {
@@ -149,7 +149,7 @@ public final class HyperRectangleReaderTest extends 
TestCase {
      * then compares against the expected values.
      */
     private void verifyRegionRead() throws IOException {
-        final short[] data = (short[]) reader.read(new Region(size, lower, 
upper, subsampling));
+        final var data = (short[]) reader.read(new Region(size, lower, upper, 
subsampling));
         int p = 0;
         final long s3 = subsampling[3];
         final long s2 = subsampling[2];


Reply via email to