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];