diff --git a/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/FitReader.java b/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/FitReader.java index cfa60aa66..3a56d9da1 100644 --- a/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/FitReader.java +++ b/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/FitReader.java @@ -26,6 +26,7 @@ import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitRecordHeader; import org.openstreetmap.josm.plugins.fit.lib.records.internal.FitRecordNormalHeader; import org.openstreetmap.josm.plugins.fit.lib.records.internal.IField; +import org.openstreetmap.josm.plugins.fit.lib.utils.CountingInputStream; import org.openstreetmap.josm.plugins.fit.lib.utils.NumberUtils; /** @@ -47,20 +48,22 @@ private FitReader() { * exception. */ public static FitData[] read(InputStream inputStream, FitReaderOptions... options) throws FitException { - final var bufferedInputStream = inputStream.markSupported() ? inputStream - : new BufferedInputStream(inputStream); + final var bufferedInputStream = new CountingInputStream( + inputStream.markSupported() ? inputStream : new BufferedInputStream(inputStream)); final var optionsSet = options.length == 0 ? EnumSet.noneOf(FitReaderOptions.class) : EnumSet.of(options[0], options); final var fitData = new ArrayList(); try { final var header = readFitHeader(bufferedInputStream); + final var headerSize = bufferedInputStream.bytesRead(); bufferedInputStream.mark(1); var localMessageHeaders = new FitDefinitionMessage[1]; var developerData = new FitDeveloperFieldDescriptionMessage[0]; long lastTimeStamp = Long.MIN_VALUE; var offsetAddition = 0; byte lastOffset = 0; - while (bufferedInputStream.read() != -1) { + while (bufferedInputStream.read() != -1 + && bufferedInputStream.bytesRead() < header.dataSize() - headerSize) { bufferedInputStream.reset(); final var nextRecordHeader = readNextRecordHeader(bufferedInputStream); if (nextRecordHeader instanceof FitRecordNormalHeader normalHeader) { @@ -124,7 +127,7 @@ public static FitData[] read(InputStream inputStream, FitReaderOptions... option } } catch (FitException fitException) { handleException(optionsSet, fitException); - } catch (IOException ioException) { + } catch (IllegalArgumentException | IOException ioException) { handleException(optionsSet, new FitException(ioException)); } return fitData.toArray(EMPTY_FIT_DATA_ARRAY); @@ -198,7 +201,7 @@ static FitDefinitionMessage readNextDefinition(FitRecordNormalHeader normalHeade final boolean littleEndian = inputStream.read() == 0; // 0 = little endian, 1 == big endian final var globalMessageNumber = NumberUtils.decodeInt(2, littleEndian, inputStream); final int numberOfFields = inputStream.read(); - final var fitFields = new ArrayList(numberOfFields); + final var fitFields = new ArrayList(Math.max(0, numberOfFields)); for (var i = 0; i < numberOfFields; i++) { fitFields.add(readNextField(inputStream)); } diff --git a/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/CountingInputStream.java b/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/CountingInputStream.java new file mode 100644 index 000000000..4c724b55b --- /dev/null +++ b/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/CountingInputStream.java @@ -0,0 +1,121 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.fit.lib.utils; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Count the bytes read from a stream + */ +public class CountingInputStream extends InputStream { + private final InputStream inputStream; + private long bytesRead; + private long mark; + + /** + * Create a new {@link CountingInputStream} + * @param inputStream The stream to count bytes read from + */ + public CountingInputStream(InputStream inputStream) { + this.inputStream = inputStream; + } + + @Override + public int read() throws IOException { + final var read = this.inputStream.read(); + if (read != -1) { + bytesRead++; + } + return read; + } + + /** + * Get the number of bytes read + * @return The bytes read + */ + public long bytesRead() { + return this.bytesRead; + } + + @Override + public int read(byte[] bytes) throws IOException { + final int read = this.inputStream.read(bytes); + if (read != -1) { + this.bytesRead += read; + } + return read; + } + + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + final int read = this.inputStream.read(bytes, off, len); + if (read != -1) { + this.bytesRead += read; + } + return read; + } + + @Override + public byte[] readAllBytes() throws IOException { + final var allBytes = this.inputStream.readAllBytes(); + this.bytesRead += allBytes.length; + return allBytes; + } + + @Override + public byte[] readNBytes(int len) throws IOException { + final var nBytes = this.inputStream.readNBytes(len); + this.bytesRead += nBytes.length; + return nBytes; + } + + @Override + public int readNBytes(byte[] b, int off, int len) throws IOException { + final var read = this.inputStream.readNBytes(b, off, len); + if (read != -1) { + this.bytesRead += read; + } + return read; + } + + @Override + public long skip(long n) throws IOException { + final var read = this.inputStream.skip(n); + this.bytesRead += read; + return read; + } + + @Override + public void skipNBytes(long n) throws IOException { + this.inputStream.skipNBytes(n); + this.bytesRead += n; // This might not be accurate... + } + + @Override + public int available() throws IOException { + return this.inputStream.available(); + } + + @Override + public void close() throws IOException { + super.close(); + this.inputStream.close(); + } + + @Override + public void mark(int readlimit) { + this.inputStream.mark(readlimit); + this.mark = this.bytesRead; + } + + @Override + public void reset() throws IOException { + this.inputStream.reset(); + this.bytesRead = this.mark; + } + + @Override + public boolean markSupported() { + return this.inputStream.markSupported(); + } +}