This is an automated email from the ASF dual-hosted git repository. iemejia pushed a commit to branch AVRO-4241-binarydecoder-verify-available-bytes-before-read in repository https://gitbox.apache.org/repos/asf/avro.git
commit f054db4b761ce67d8f1fcfa568e488471adbeff8 Author: Ismaël Mejía <[email protected]> AuthorDate: Thu Apr 30 20:43:17 2026 +0200 AVRO-4241: [Java] Simplify no-op validatioin code and fix possible int overflow per PR comments --- .../apache/avro/generic/GenericDatumReader.java | 32 ++++++++++------------ .../java/org/apache/avro/io/BinaryDecoder.java | 3 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java b/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java index e09dc95d2d..178ca50cca 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java +++ b/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java @@ -324,9 +324,7 @@ public class GenericDatumReader<D> implements DatumReader<D> { */ private long arrayNext(ResolvingDecoder in, Schema elementType) throws IOException { long l = in.arrayNext(); - if (l > 0) { - ensureAvailableCollectionBytes(in, l, elementType); - } + ensureAvailableCollectionBytes(in, l, elementType); return l; } @@ -365,9 +363,7 @@ public class GenericDatumReader<D> implements DatumReader<D> { long l = in.readMapStart(); LogicalType logicalType = eValue.getLogicalType(); Conversion<?> conversion = getData().getConversionFor(logicalType); - if (l > 0) { - ensureAvailableMapBytes(in, l, eValue); - } + ensureAvailableMapBytes(in, l, eValue); Object map = newMap(old, (int) l); if (l > 0) { do { @@ -392,9 +388,7 @@ public class GenericDatumReader<D> implements DatumReader<D> { */ private long mapNext(ResolvingDecoder in, Schema valueType) throws IOException { long l = in.mapNext(); - if (l > 0) { - ensureAvailableMapBytes(in, l, valueType); - } + ensureAvailableMapBytes(in, l, valueType); return l; } @@ -404,14 +398,15 @@ public class GenericDatumReader<D> implements DatumReader<D> { * per entry is {@code 1 + minBytesPerElement(valueSchema)}. */ private static void ensureAvailableMapBytes(Decoder decoder, long count, Schema valueSchema) throws EOFException { + if (count <= 0) { + return; + } // Map keys are always strings: at least 1 byte for the length varint - int minBytesPerEntry = 1 + minBytesPerElement(valueSchema); - if (count > 0) { - int remaining = decoder.remainingBytes(); - if (remaining >= 0 && count * (long) minBytesPerEntry > remaining) { - throw new EOFException("Map claims " + count + " entries with at least " + minBytesPerEntry - + " bytes each, but only " + remaining + " bytes are available"); - } + long minBytesPerEntry = 1L + minBytesPerElement(valueSchema); + int remaining = decoder.remainingBytes(); + if (remaining >= 0 && count * minBytesPerEntry > remaining) { + throw new EOFException("Map claims " + count + " entries with at least " + minBytesPerEntry + + " bytes each, but only " + remaining + " bytes are available"); } } @@ -490,8 +485,11 @@ public class GenericDatumReader<D> implements DatumReader<D> { */ private static void ensureAvailableCollectionBytes(Decoder decoder, long count, Schema elementSchema) throws EOFException { + if (count <= 0) { + return; + } int minBytes = minBytesPerElement(elementSchema); - if (minBytes > 0 && count > 0) { + if (minBytes > 0) { int remaining = decoder.remainingBytes(); if (remaining >= 0 && count * (long) minBytes > remaining) { throw new EOFException("Collection claims " + count + " elements with at least " + minBytes diff --git a/lang/java/avro/src/main/java/org/apache/avro/io/BinaryDecoder.java b/lang/java/avro/src/main/java/org/apache/avro/io/BinaryDecoder.java index 22d86ca650..77fc849076 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/io/BinaryDecoder.java +++ b/lang/java/avro/src/main/java/org/apache/avro/io/BinaryDecoder.java @@ -961,7 +961,8 @@ public class BinaryDecoder extends Decoder { int buffered = ba.getLim() - ba.getPos(); try { if (in.getClass() == ByteArrayInputStream.class || in.getClass() == ByteBufferInputStream.class) { - return buffered + in.available(); + long total = (long) buffered + in.available(); + return (int) Math.min(total, Integer.MAX_VALUE); } } catch (IOException e) { return -1;
