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

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/main by this push:
     new 3508f0ec8d AVRO-4228: [c++] Fix BinaryDecoder::arrayNext() to handle 
negative block counts (#3646)
3508f0ec8d is described below

commit 3508f0ec8d90038a96133d018c93e8f52e925264
Author: gfeyer <[email protected]>
AuthorDate: Mon Feb 16 06:01:48 2026 -0500

    AVRO-4228: [c++] Fix BinaryDecoder::arrayNext() to handle negative block 
counts (#3646)
    
    * AVRO-4228: Fix BinaryDecoder::arrayNext() to handle negative block counts
    
    * AVRO-4228: Add test for arrayNext() with negative block counts
    
    * AVRO-4228: Move negative block count to second block in test
    
    * AVRO-4228: Avoid undefined behavior when negating INT64_MIN in 
doDecodeItemCount
    
    ---------
    
    Co-authored-by: Gabriel Feyer <[email protected]>
---
 lang/c++/impl/BinaryDecoder.cc |  4 ++--
 lang/c++/test/CodecTests.cc    | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/lang/c++/impl/BinaryDecoder.cc b/lang/c++/impl/BinaryDecoder.cc
index a970d60520..b334de7cf5 100644
--- a/lang/c++/impl/BinaryDecoder.cc
+++ b/lang/c++/impl/BinaryDecoder.cc
@@ -164,13 +164,13 @@ size_t BinaryDecoder::doDecodeItemCount() {
     auto result = doDecodeLong();
     if (result < 0) {
         doDecodeLong();
-        return static_cast<size_t>(-result);
+        return static_cast<size_t>(-(result + 1)) + 1;
     }
     return static_cast<size_t>(result);
 }
 
 size_t BinaryDecoder::arrayNext() {
-    return static_cast<size_t>(doDecodeLong());
+    return doDecodeItemCount();
 }
 
 size_t BinaryDecoder::skipArray() {
diff --git a/lang/c++/test/CodecTests.cc b/lang/c++/test/CodecTests.cc
index fb88877836..59aa023dbd 100644
--- a/lang/c++/test/CodecTests.cc
+++ b/lang/c++/test/CodecTests.cc
@@ -2100,6 +2100,40 @@ static void testJsonCodecReinit() {
     }
 }
 
+static void testArrayNegativeBlockCount() {
+    // Array of ints [10, 20, 30, 40, 50] encoded with a negative block count
+    // in the second block, which exercises arrayNext().
+    // Per the Avro spec, a negative count means: abs(count) items follow,
+    // preceded by a long byte-size of the block.
+    //
+    // Block 1: count=2, items: 10, 20              (read by arrayStart)
+    // Block 2: count=-3, bytesize=3, items: 30, 40, 50  (read by arrayNext)
+    // Terminal: count=0
+    const uint8_t data[] = {
+        0x04,                    // zigzag(2) = 4: block count = 2
+        0x14, 0x28,             // zigzag ints: 10, 20
+        0x05,                    // zigzag(-3) = 5: block count = -3
+        0x06,                    // zigzag(3) = 6: byte-size of block
+        0x3c, 0x50, 0x64,       // zigzag ints: 30, 40, 50
+        0x00                     // terminal
+    };
+
+    InputStreamPtr is = memoryInputStream(data, sizeof(data));
+    DecoderPtr d = binaryDecoder();
+    d->init(*is);
+
+    std::vector<int32_t> result;
+    for (size_t n = d->arrayStart(); n != 0; n = d->arrayNext()) {
+        for (size_t i = 0; i < n; ++i) {
+            result.push_back(d->decodeInt());
+        }
+    }
+
+    const std::vector<int32_t> expected = {10, 20, 30, 40, 50};
+    BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
+                                  expected.begin(), expected.end());
+}
+
 static void testByteCount() {
     OutputStreamPtr os1 = memoryOutputStream();
     EncoderPtr e1 = binaryEncoder();
@@ -2125,6 +2159,7 @@ init_unit_test_suite(int, char *[]) {
     ts->add(BOOST_PARAM_TEST_CASE(&avro::testJson, avro::jsonData,
                                   ENDOF(avro::jsonData)));
     ts->add(BOOST_TEST_CASE(avro::testJsonCodecReinit));
+    ts->add(BOOST_TEST_CASE(avro::testArrayNegativeBlockCount));
     ts->add(BOOST_TEST_CASE(avro::testByteCount));
 
     return ts;

Reply via email to