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

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new f2376c46f8 Fix a buffer used when inflating tile data in HEIF.
f2376c46f8 is described below

commit f2376c46f8cb42595b014f4e6a53b371d7abcc17
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Jun 19 17:06:08 2026 +0200

    Fix a buffer used when inflating tile data in HEIF.
---
 .../sis/storage/geotiff/inflater/Inflater.java     |  2 +-
 .../io/stream/inflater/CompressionException.java   |  6 ++++
 .../io/stream/inflater/ComputedByteChannel.java    |  7 ++---
 .../org/apache/sis/io/stream/inflater/Deflate.java |  2 +-
 .../apache/sis/storage/geoheif/ImageResource.java  | 36 ++++++++++++----------
 .../sis/storage/geoheif/UncompressedImage.java     |  9 ++++--
 6 files changed, 35 insertions(+), 27 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/Inflater.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/Inflater.java
index 05b805181d..409f1bfa10 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/Inflater.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/Inflater.java
@@ -238,7 +238,7 @@ public abstract class Inflater implements Closeable {
             }
         }
         final int scanlineStride = Math.multiplyExact(sourceWidth, 
sourcePixelStride * dataType.bytes());
-        final ChannelDataInput inflated = channel.createDataInput(null, 
scanlineStride);
+        final ChannelDataInput inflated = 
channel.createDataInput(scanlineStride);
         return CopyFromBytes.create(inflated, dataType, chunksPerRow, 
samplesPerChunk, skipAfterChunks, pixelsPerElement);
     }
 
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/CompressionException.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/CompressionException.java
index bbd7626981..f567cb4643 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/CompressionException.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/CompressionException.java
@@ -30,6 +30,12 @@ public class CompressionException extends IOException {
      */
     private static final long serialVersionUID = -5493223452276185518L;
 
+    /**
+     * Constructs a new exception with no detail message and no cause.
+     */
+    public CompressionException() {
+    }
+
     /**
      * Constructs a new exception with the specified detail message and cause.
      *
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/ComputedByteChannel.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/ComputedByteChannel.java
index 95c92da1da..3e80e1fab1 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/ComputedByteChannel.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/ComputedByteChannel.java
@@ -91,12 +91,11 @@ public abstract class ComputedByteChannel implements 
ReadableByteChannel {
      * for performance reasons. A well adjusted buffer size reduces calls to 
{@link ByteBuffer#compact()},
      * which in turn reduces the number of copy operations between different 
regions of the buffer.</p>
      *
-     * @param  buffer          buffer to reuse if {@code null}. The content of 
this buffer will be discarded.
      * @param  scanlineStride  the scanline stride of the image to read. Used 
for choosing a buffer size.
      * @throws IOException if an error occurred while filling the buffer with 
initial data.
      * @return the data input for uncompressed data.
      */
-    public final ChannelDataInput createDataInput(ByteBuffer buffer, final int 
scanlineStride) throws IOException {
+    public final ChannelDataInput createDataInput(final int scanlineStride) 
throws IOException {
         final int capacity;
         if (scanlineStride > BUFFER_SIZE) {
             final int[] divisors = MathFunctions.divisors(scanlineStride);
@@ -109,9 +108,7 @@ public abstract class ComputedByteChannel implements 
ReadableByteChannel {
             capacity = BUFFER_SIZE;
         }
         final ChannelDataInput input = compressedInput();
-        if (buffer == null) {
-            buffer = preferNativeBuffer() ? 
ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity);
-        }
+        ByteBuffer buffer = preferNativeBuffer() ? 
ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity);
         buffer = buffer.order(input.buffer.order()).limit(0);
         return new ChannelDataInput(input.filename, this, buffer, true);
     }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/Deflate.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/Deflate.java
index 1db05bd9dd..47917355d9 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/Deflate.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/inflater/Deflate.java
@@ -100,7 +100,7 @@ public final class Deflate extends InflaterChannel {
                 } else if (inflater.finished()) {
                     return -1;
                 } else {
-                    throw new IOException();
+                    throw new CompressionException();
                 }
             }
         } catch (DataFormatException e) {
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
index c1a0f9ee4b..76f9879804 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
@@ -22,7 +22,6 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
-import java.nio.ByteBuffer;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import static java.lang.Math.addExact;
@@ -48,6 +47,7 @@ import org.apache.sis.storage.tiling.TiledGridCoverage;
 import org.apache.sis.storage.tiling.TiledGridCoverageResource;
 import org.apache.sis.storage.isobmff.ByteRanges;
 import org.apache.sis.io.stream.ChannelDataInput;
+import org.apache.sis.io.stream.inflater.ComputedByteChannel;
 import org.apache.sis.util.internal.shared.Numerics;
 
 
@@ -324,15 +324,15 @@ final class ImageResource extends 
TiledGridCoverageResource implements StoreReso
             final var requests = new ReadContext[result.length];
             int count = 0;
             synchronized (getSynchronizationLock()) {
-                final var readers = new HashMap<ImageReaderSpi, ImageReader>();
-                final var buffer  = new AtomicReference<ByteBuffer>();
+                final var readers  = new HashMap<ImageReaderSpi, 
ImageReader>();
+                final var inflater = new 
AtomicReference<ComputedByteChannel>();
                 try {
                     do {
                         Raster raster = iterator.getCachedTile();
                         if (raster != null) {
                             result[iterator.getTileIndexInResultArray()] = 
raster;
                         } else {
-                            requests[count++] = new ReadContext(iterator, 
readers, buffer, ImageResource.this);
+                            requests[count++] = new ReadContext(iterator, 
readers, inflater, ImageResource.this);
                         }
                     } while (iterator.next());
                     /*
@@ -361,6 +361,8 @@ final class ImageResource extends TiledGridCoverageResource 
implements StoreReso
         /**
          * Context about a {@code readTile(…)} operation. Contains the tile to 
create, or
          * the image reader to use in the case of read operations delegated to 
Image I/O.
+         * An instance of this class exist for each tile and is discarded 
after the tile
+         * has been read.
          */
         static final class ReadContext extends ByteRanges {
             /**
@@ -385,29 +387,29 @@ final class ImageResource extends 
TiledGridCoverageResource implements StoreReso
             final long subTileX, subTileY;
 
             /**
-             * Buffer to reuse for each tile.
+             * Inflater to reuse for each tile.
              */
-            private final AtomicReference<ByteBuffer> buffer;
+            private final AtomicReference<ComputedByteChannel> inflater;
 
             /**
              * Creates a new read context.
              *
              * @param  iterator  iterator over the tiles to read.
              * @param  readers   an initially empty map where to store image 
readers for reuse.
-             * @param  buffer    an initially empty reference to a buffer.
+             * @param  inflater  an initially empty reference to an inflater 
to recycle.
              * @param  owner     the resource for which to read a tile.
              * @throws DataStoreException if an error occurred while computing 
the range of bytes.
              */
             @SuppressWarnings("LeakingThisInConstructor")
             private ReadContext(final AOI iterator,
                                 final Map<ImageReaderSpi, ImageReader> readers,
-                                final AtomicReference<ByteBuffer> buffer,
+                                final AtomicReference<ComputedByteChannel> 
inflater,
                                 final ImageResource owner)
                     throws DataStoreException
             {
                 this.iterator = new Snapshot(iterator);
                 this.readers  = readers;
-                this.buffer   = buffer;
+                this.inflater = inflater;
                 final long[] tileCoord = 
iterator.getTileCoordinatesInResource();
                 final Image tile = owner.getTile(tileCoord[0], tileCoord[1]);
                 subTileX = tileCoord[0] % tile.numXTiles;
@@ -465,21 +467,21 @@ final class ImageResource extends 
TiledGridCoverageResource implements StoreReso
             }
 
             /**
-             * If a buffer has already been used in a previous read operation, 
returns that buffer.
+             * If a inflater has already been used in a previous read 
operation, returns that inflater.
              *
-             * @return buffer that can be reused, or {@code null} if none.
+             * @return inflater that can be reused, or {@code null} if none.
              */
-            public ByteBuffer reuseBuffer() {
-                return buffer.getAndSet(null);
+            public ComputedByteChannel reuseInflater() {
+                return inflater.getAndSet(null);
             }
 
             /**
-             * Saves the given buffer for reuse.
+             * Saves the given inflater for reuse.
              *
-             * @param  done  a buffer which is no longer needed.
+             * @param  done  a inflater which is no longer needed.
              */
-            public void saveForReuse(final ByteBuffer done) {
-                buffer.set(done);
+            public void saveForReuse(final ComputedByteChannel done) {
+                inflater.set(done);
             }
         }
     }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
index ebdbdfd590..db2fc606a3 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
@@ -189,12 +189,15 @@ class UncompressedImage extends Image {
         computeByteRanges(tileIndex, region, context);
         return (ChannelDataInput input) -> {
             long origin = context.offset();
-            final ComputedByteChannel inflater = inflater(input);
+            ComputedByteChannel inflater = context.reuseInflater();
+            if (inflater == null || inflater.compressedInput() != input) {
+                inflater = inflater(input);
+            }
             if (inflater != null) {
                 final CompressedUnitsItemInfo.Unit unit = 
compressedImageUnit(tileIndex);
                 inflater.setInputRegion(addExact(origin, unit.offset), 
unit.size);
                 // The following (int) cast is okay even if inexact because it 
is only a hint.
-                input = inflater.createDataInput(context.reuseBuffer(), (int) 
sourceSize[0]);
+                input = inflater.createDataInput((int) sourceSize[0]);
                 origin = 0;
             }
             /*
@@ -221,7 +224,7 @@ class UncompressedImage extends Image {
                 hr.readAsBuffer(bank, 0);
             }
             if (inflater != null) {
-                context.saveForReuse(inflater.compressedInput().buffer);
+                context.saveForReuse(inflater);
             }
             return raster;
         };

Reply via email to