From 06c4fc9348ef182b8adbac741407dd826988de05 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Tue, 20 Oct 2026 08:24:56 +0100 Subject: [PATCH 1/2] OSS-Fuzz: Update parquet encoding fuzzer and dict/corpora Signed-off-by: Arthur Chan --- .../fuzzing/dicts/parquet-encoding-fuzz.dict | 122 +++++++++++++++ .../parquet/arrow/fuzz_encoding_internal.cc | 144 ++++++++++++++++-- .../parquet/arrow/fuzz_encoding_internal.h | 9 +- .../arrow/generate_encoding_fuzz_corpus.cc | 85 ++++++++++- 4 files changed, 348 insertions(+), 12 deletions(-) create mode 100644 cpp/build-support/fuzzing/dicts/parquet-encoding-fuzz.dict diff --git a/cpp/build-support/fuzzing/dicts/parquet-encoding-fuzz.dict b/cpp/build-support/fuzzing/dicts/parquet-encoding-fuzz.dict new file mode 100644 index 000000000000..46ac8d4ead7d --- /dev/null +++ b/cpp/build-support/fuzzing/dicts/parquet-encoding-fuzz.dict @@ -0,0 +1,122 @@ +# Tokens for parquet-encoding-fuzz: packed 64-byte fuzz headers +# covering every supported (physical type, source encoding, roundtrip +# encoding) combination of the encoding matrix, plus dictionary- +# roundtrip variants (flags byte = 1). num_values is set to 64. +BOOLEAN-PLAIN-PLAIN="\x00\x00\x00\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-PLAIN-PLAIN-dict="\x00\x00\x00\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-PLAIN-RLE="\x00\x03\x00\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-PLAIN-RLE-dict="\x00\x03\x00\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-RLE-PLAIN="\x03\x00\x00\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-RLE-PLAIN-dict="\x03\x00\x00\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-RLE-RLE="\x03\x03\x00\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BOOLEAN-RLE-RLE-dict="\x03\x03\x00\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-PLAIN-PLAIN="\x00\x00\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-PLAIN-PLAIN-dict="\x00\x00\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-PLAIN-DELTA_BINARY_PACKED="\x00\x05\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-PLAIN-DELTA_BINARY_PACKED-dict="\x00\x05\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-PLAIN-BYTE_STREAM_SPLIT="\x00\x09\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-PLAIN-BYTE_STREAM_SPLIT-dict="\x00\x09\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-DELTA_BINARY_PACKED-PLAIN="\x05\x00\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-DELTA_BINARY_PACKED-PLAIN-dict="\x05\x00\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-DELTA_BINARY_PACKED-DELTA_BINARY_PACKED="\x05\x05\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-DELTA_BINARY_PACKED-DELTA_BINARY_PACKED-dict="\x05\x05\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-DELTA_BINARY_PACKED-BYTE_STREAM_SPLIT="\x05\x09\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-DELTA_BINARY_PACKED-BYTE_STREAM_SPLIT-dict="\x05\x09\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-BYTE_STREAM_SPLIT-PLAIN="\x09\x00\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-BYTE_STREAM_SPLIT-PLAIN-dict="\x09\x00\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-BYTE_STREAM_SPLIT-DELTA_BINARY_PACKED="\x09\x05\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-BYTE_STREAM_SPLIT-DELTA_BINARY_PACKED-dict="\x09\x05\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT="\x09\x09\x01\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT32-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT-dict="\x09\x09\x01\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-PLAIN-PLAIN="\x00\x00\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-PLAIN-PLAIN-dict="\x00\x00\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-PLAIN-DELTA_BINARY_PACKED="\x00\x05\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-PLAIN-DELTA_BINARY_PACKED-dict="\x00\x05\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-PLAIN-BYTE_STREAM_SPLIT="\x00\x09\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-PLAIN-BYTE_STREAM_SPLIT-dict="\x00\x09\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-DELTA_BINARY_PACKED-PLAIN="\x05\x00\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-DELTA_BINARY_PACKED-PLAIN-dict="\x05\x00\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-DELTA_BINARY_PACKED-DELTA_BINARY_PACKED="\x05\x05\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-DELTA_BINARY_PACKED-DELTA_BINARY_PACKED-dict="\x05\x05\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-DELTA_BINARY_PACKED-BYTE_STREAM_SPLIT="\x05\x09\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-DELTA_BINARY_PACKED-BYTE_STREAM_SPLIT-dict="\x05\x09\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-BYTE_STREAM_SPLIT-PLAIN="\x09\x00\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-BYTE_STREAM_SPLIT-PLAIN-dict="\x09\x00\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-BYTE_STREAM_SPLIT-DELTA_BINARY_PACKED="\x09\x05\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-BYTE_STREAM_SPLIT-DELTA_BINARY_PACKED-dict="\x09\x05\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT="\x09\x09\x02\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT64-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT-dict="\x09\x09\x02\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT96-PLAIN-PLAIN="\x00\x00\x03\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +INT96-PLAIN-PLAIN-dict="\x00\x00\x03\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-PLAIN-PLAIN="\x00\x00\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-PLAIN-PLAIN-dict="\x00\x00\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-PLAIN-BYTE_STREAM_SPLIT="\x00\x09\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-PLAIN-BYTE_STREAM_SPLIT-dict="\x00\x09\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-PLAIN-ALP="\x00\x0a\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-PLAIN-ALP-dict="\x00\x0a\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-BYTE_STREAM_SPLIT-PLAIN="\x09\x00\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-BYTE_STREAM_SPLIT-PLAIN-dict="\x09\x00\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT="\x09\x09\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT-dict="\x09\x09\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-BYTE_STREAM_SPLIT-ALP="\x09\x0a\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-BYTE_STREAM_SPLIT-ALP-dict="\x09\x0a\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-ALP-PLAIN="\x0a\x00\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-ALP-PLAIN-dict="\x0a\x00\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-ALP-BYTE_STREAM_SPLIT="\x0a\x09\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-ALP-BYTE_STREAM_SPLIT-dict="\x0a\x09\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-ALP-ALP="\x0a\x0a\x04\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FLOAT-ALP-ALP-dict="\x0a\x0a\x04\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-PLAIN-PLAIN="\x00\x00\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-PLAIN-PLAIN-dict="\x00\x00\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-PLAIN-BYTE_STREAM_SPLIT="\x00\x09\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-PLAIN-BYTE_STREAM_SPLIT-dict="\x00\x09\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-PLAIN-ALP="\x00\x0a\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-PLAIN-ALP-dict="\x00\x0a\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-BYTE_STREAM_SPLIT-PLAIN="\x09\x00\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-BYTE_STREAM_SPLIT-PLAIN-dict="\x09\x00\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT="\x09\x09\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT-dict="\x09\x09\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-BYTE_STREAM_SPLIT-ALP="\x09\x0a\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-BYTE_STREAM_SPLIT-ALP-dict="\x09\x0a\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-ALP-PLAIN="\x0a\x00\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-ALP-PLAIN-dict="\x0a\x00\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-ALP-BYTE_STREAM_SPLIT="\x0a\x09\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-ALP-BYTE_STREAM_SPLIT-dict="\x0a\x09\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-ALP-ALP="\x0a\x0a\x05\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +DOUBLE-ALP-ALP-dict="\x0a\x0a\x05\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-PLAIN-PLAIN="\x00\x00\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-PLAIN-PLAIN-dict="\x00\x00\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-PLAIN-DELTA_LENGTH_BYTE_ARRAY="\x00\x06\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-PLAIN-DELTA_LENGTH_BYTE_ARRAY-dict="\x00\x06\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-PLAIN-DELTA_BYTE_ARRAY="\x00\x07\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-PLAIN-DELTA_BYTE_ARRAY-dict="\x00\x07\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-PLAIN="\x06\x00\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-PLAIN-dict="\x06\x00\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY="\x06\x06\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-dict="\x06\x06\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-DELTA_BYTE_ARRAY="\x06\x07\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-DELTA_BYTE_ARRAY-dict="\x06\x07\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_BYTE_ARRAY-PLAIN="\x07\x00\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_BYTE_ARRAY-PLAIN-dict="\x07\x00\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY="\x07\x06\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_BYTE_ARRAY-DELTA_LENGTH_BYTE_ARRAY-dict="\x07\x06\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_BYTE_ARRAY-DELTA_BYTE_ARRAY="\x07\x07\x06\xff\xff\xff\xff\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +BYTE_ARRAY-DELTA_BYTE_ARRAY-DELTA_BYTE_ARRAY-dict="\x07\x07\x06\xff\xff\xff\xff\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-PLAIN-PLAIN="\x00\x00\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-PLAIN-PLAIN-dict="\x00\x00\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-PLAIN-BYTE_STREAM_SPLIT="\x00\x09\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-PLAIN-BYTE_STREAM_SPLIT-dict="\x00\x09\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-PLAIN-DELTA_BYTE_ARRAY="\x00\x07\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-PLAIN-DELTA_BYTE_ARRAY-dict="\x00\x07\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-BYTE_STREAM_SPLIT-PLAIN="\x09\x00\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-BYTE_STREAM_SPLIT-PLAIN-dict="\x09\x00\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT="\x09\x09\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-BYTE_STREAM_SPLIT-BYTE_STREAM_SPLIT-dict="\x09\x09\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-BYTE_STREAM_SPLIT-DELTA_BYTE_ARRAY="\x09\x07\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-BYTE_STREAM_SPLIT-DELTA_BYTE_ARRAY-dict="\x09\x07\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-DELTA_BYTE_ARRAY-PLAIN="\x07\x00\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-DELTA_BYTE_ARRAY-PLAIN-dict="\x07\x00\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-DELTA_BYTE_ARRAY-BYTE_STREAM_SPLIT="\x07\x09\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-DELTA_BYTE_ARRAY-BYTE_STREAM_SPLIT-dict="\x07\x09\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-DELTA_BYTE_ARRAY-DELTA_BYTE_ARRAY="\x07\x07\x07\x10\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +FIXED_LEN_BYTE_ARRAY-DELTA_BYTE_ARRAY-DELTA_BYTE_ARRAY-dict="\x07\x07\x07\x10\x00\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" diff --git a/cpp/src/parquet/arrow/fuzz_encoding_internal.cc b/cpp/src/parquet/arrow/fuzz_encoding_internal.cc index 9b8917897c02..926406cc6ae0 100644 --- a/cpp/src/parquet/arrow/fuzz_encoding_internal.cc +++ b/cpp/src/parquet/arrow/fuzz_encoding_internal.cc @@ -18,6 +18,7 @@ #include "parquet/arrow/fuzz_encoding_internal.h" #include +#include #include #include #include @@ -69,6 +70,10 @@ namespace { constexpr auto kPackedEncodingHeaderSize = 64; +/// Header flag: roundtrip the decoded values through the dictionary +/// encoders/decoders as well. +constexpr uint8_t kFuzzEncodingFlagDictionary = 1; + ARROW_PACKED_START(struct, PackedEncodingHeader) { ARROW_PACKED_START(struct, Header) { uint8_t source_encoding_id; @@ -76,10 +81,11 @@ ARROW_PACKED_START(struct, PackedEncodingHeader) { uint8_t type_id; int32_t type_length; int32_t num_values; + uint8_t flags; }; ARROW_PACKED_END - static_assert(sizeof(Header) == 3 * 1 + 2 * 4); + static_assert(sizeof(Header) == 3 * 1 + 2 * 4 + 1); using Reserved = std::array; Header header; @@ -93,18 +99,21 @@ static_assert(sizeof(PackedEncodingHeader) == kPackedEncodingHeaderSize); FuzzEncodingHeader::FuzzEncodingHeader(Encoding::type source_encoding, Encoding::type roundtrip_encoding, Type::type type, - int type_length, int num_values) + int type_length, int num_values, + bool use_dictionary) : source_encoding(source_encoding), roundtrip_encoding(roundtrip_encoding), type(type), type_length(type_length), - num_values(num_values) {} + num_values(num_values), + use_dictionary(use_dictionary) {} FuzzEncodingHeader::FuzzEncodingHeader(Encoding::type source_encoding, Encoding::type roundtrip_encoding, - const ColumnDescriptor* descr, int num_values) + const ColumnDescriptor* descr, int num_values, + bool use_dictionary) : FuzzEncodingHeader(source_encoding, roundtrip_encoding, descr->physical_type(), - descr->type_length(), num_values) {} + descr->type_length(), num_values, use_dictionary) {} std::string FuzzEncodingHeader::Serialize() const { PackedEncodingHeader packed{}; @@ -113,6 +122,7 @@ std::string FuzzEncodingHeader::Serialize() const { packed.header.type_id = static_cast(type); packed.header.type_length = type_length; packed.header.num_values = num_values; + packed.header.flags = use_dictionary ? kFuzzEncodingFlagDictionary : 0; return std::string(reinterpret_cast(&packed), kPackedEncodingHeaderSize); } @@ -134,6 +144,9 @@ ::arrow::Result FuzzEncodingHeader::Parse( return invalid_payload(); } const auto& ph = packed.header; + if (ph.flags & ~kFuzzEncodingFlagDictionary) { + return invalid_payload(); + } if (ph.source_encoding_id >= static_cast(Encoding::UNDEFINED) || ph.roundtrip_encoding_id >= static_cast(Encoding::UNDEFINED) || ph.type_id >= static_cast(Type::UNDEFINED)) { @@ -142,7 +155,8 @@ ::arrow::Result FuzzEncodingHeader::Parse( FuzzEncodingHeader header(static_cast(ph.source_encoding_id), static_cast(ph.roundtrip_encoding_id), static_cast(ph.type_id), ph.type_length, - ph.num_values); + ph.num_values, + (ph.flags & kFuzzEncodingFlagDictionary) != 0); if (!IsEncodingSupported(header.type, header.source_encoding) || !IsEncodingSupported(header.type, header.roundtrip_encoding)) { return invalid_payload(); @@ -187,12 +201,13 @@ struct TypedFuzzEncoding { TypedFuzzEncoding(Encoding::type source_encoding, Encoding::type roundtrip_encoding, const ColumnDescriptor* descr, int num_values, - std::span encoded_data) + std::span encoded_data, bool use_dictionary = false) : source_encoding_(source_encoding), roundtrip_encoding_(roundtrip_encoding), descr_(descr), num_values_(num_values), - encoded_data_(encoded_data) {} + encoded_data_(encoded_data), + use_dictionary_(use_dictionary) {} // Decoders of string-like types return pointers into the // decoder's internal scratch space, which get invalidated on the @@ -238,6 +253,41 @@ struct TypedFuzzEncoding { return Status::OK(); } + // Same as RunOnDecodedChunks, but with a decoder that has already been + // prepared (i.e. SetData() has already been called on it). This is required + // for decoders that need extra setup, such as dictionary decoders. + Status RunOnPreparedDecoderChunks( + TypedDecoder* decoder, int chunk_size, + std::function)> func) { + BEGIN_PARQUET_CATCH_EXCEPTIONS + int total_values = 0; + while (total_values < num_values_) { + const int read_size = std::min(num_values_ - total_values, chunk_size); + PoolVector chunk_values(pool()); + BEGIN_CATCH_BAD_ALLOC + chunk_values.resize(read_size); + END_CATCH_BAD_ALLOC + int values_read; + if constexpr (kType == Type::BOOLEAN) { + values_read = + decoder->Decode(reinterpret_cast(chunk_values.data()), read_size); + } else { + values_read = decoder->Decode(chunk_values.data(), read_size); + } + ARROW_CHECK_LE(values_read, read_size); + RETURN_NOT_OK(func(total_values, std::span(chunk_values).first(values_read))); + total_values += values_read; + if (values_read < chunk_size) { + break; + } + } + if (total_values < num_values_) { + return Status::Invalid("Read less values than expected"); + } + END_PARQUET_CATCH_EXCEPTIONS + return Status::OK(); + } + Result> Decode(Encoding::type encoding, std::span encoded_data, int chunk_size) { @@ -284,6 +334,64 @@ struct TypedFuzzEncoding { return output; } + // Roundtrip the reference values through the dictionary encoders and + // decoders, which the regular encoding matrix above does not cover. + // The dictionary page is fed through a PLAIN decoder in between, mirroring + // how a real Parquet reader consumes a dictionary page. + Status FuzzDictionaryRoundtrip() { + BEGIN_PARQUET_CATCH_EXCEPTIONS + auto encoder = MakeTypedEncoder(Encoding::PLAIN_DICTIONARY, + /*use_dictionary=*/true, descr_, pool()); + auto* dict_encoder = dynamic_cast*>(encoder.get()); + if (dict_encoder == nullptr) { + // Dictionary encoding is not available for this type + return Status::OK(); + } + if constexpr (arrow_supported()) { + dict_encoder->Put(*reference_array_); + } else { + return Status::OK(); + } + + // Write out the dictionary page + const int dict_size = dict_encoder->dict_encoded_size(); + PoolVector dict_buffer(pool()); + BEGIN_CATCH_BAD_ALLOC + dict_buffer.resize(dict_size); + END_CATCH_BAD_ALLOC + dict_encoder->WriteDict(dict_buffer.data()); + + // Write out the indices page + const auto indices_capacity = + std::max(dict_encoder->EstimatedDataEncodedSize(), 16); + PoolVector indices_buffer(pool()); + BEGIN_CATCH_BAD_ALLOC + indices_buffer.resize(indices_capacity); + END_CATCH_BAD_ALLOC + const int indices_size = dict_encoder->WriteIndices( + indices_buffer.data(), static_cast(indices_buffer.size())); + ARROW_CHECK_GE(indices_size, 0); + + // Decode the dictionary page with a PLAIN decoder, as a real reader does + auto dict_page_decoder = MakeDecoder(Encoding::PLAIN); + dict_page_decoder->SetData(dict_encoder->num_entries(), dict_buffer.data(), + dict_size); + + auto dict_decoder = MakeDictDecoder(descr_, pool()); + dict_decoder->SetDict(dict_page_decoder.get()); + dict_decoder->SetData(num_values_, indices_buffer.data(), indices_size); + + auto compare_chunk = [&](int offset, std::span chunk_values) { + return CompareChunkAgainstReference(offset, chunk_values); + }; + for (const int chunk_size : chunk_sizes()) { + RETURN_NOT_OK(RunOnPreparedDecoderChunks(dict_decoder.get(), chunk_size, + compare_chunk)); + } + END_PARQUET_CATCH_EXCEPTIONS + return Status::OK(); + } + Status Fuzz() { // Decode using source encoding if constexpr (arrow_supported()) { @@ -335,6 +443,22 @@ struct TypedFuzzEncoding { if (!roundtrip_status.IsOutOfMemory()) { ARROW_CHECK_OK(roundtrip_status); } + + // Optionally roundtrip the reference values through the dictionary + // encoders/decoders as well, which is not exercised by the regular + // encoding matrix above. + if (use_dictionary_) { + if constexpr (arrow_supported()) { + if (num_values_ > 0) { + Status dict_status = FuzzDictionaryRoundtrip(); + // OOM when attempting to roundtrip is not a hard failure, any other + // error is. + if (!dict_status.IsOutOfMemory()) { + ARROW_CHECK_OK(dict_status); + } + } + } + } return roundtrip_status; } @@ -490,6 +614,7 @@ struct TypedFuzzEncoding { const Encoding::type source_encoding_, roundtrip_encoding_; const ColumnDescriptor* descr_; const int num_values_; + const bool use_dictionary_; const std::span encoded_data_; std::shared_ptr reference_array_; @@ -521,7 +646,8 @@ FuzzStatus FuzzEncoding(const uint8_t* data, int64_t size) { auto typed_fuzz = [&](auto* dtype) { using DType = std::decay_t; TypedFuzzEncoding typed_fuzz{header.source_encoding, header.roundtrip_encoding, - &descr, header.num_values, encoded_data}; + &descr, header.num_values, encoded_data, + header.use_dictionary}; return typed_fuzz.Fuzz(); }; RETURN_NOT_OK(VisitType(header.type, typed_fuzz)); diff --git a/cpp/src/parquet/arrow/fuzz_encoding_internal.h b/cpp/src/parquet/arrow/fuzz_encoding_internal.h index 8ce17eadc41c..f0193c262d9b 100644 --- a/cpp/src/parquet/arrow/fuzz_encoding_internal.h +++ b/cpp/src/parquet/arrow/fuzz_encoding_internal.h @@ -60,11 +60,16 @@ struct FuzzEncodingHeader { int32_t type_length; /// The number of encoded values int32_t num_values; + /// Whether to also roundtrip the decoded values through the dictionary + /// encoders/decoders (in addition to `roundtrip_encoding`) + bool use_dictionary; FuzzEncodingHeader(Encoding::type source_encoding, Encoding::type roundtrip_encoding, - Type::type type, int type_length, int num_values); + Type::type type, int type_length, int num_values, + bool use_dictionary = false); FuzzEncodingHeader(Encoding::type source_encoding, Encoding::type roundtrip_encoding, - const ColumnDescriptor* descr, int num_values); + const ColumnDescriptor* descr, int num_values, + bool use_dictionary = false); /// Serialize as a fixed size header for fuzz corpus generation std::string Serialize() const; diff --git a/cpp/src/parquet/arrow/generate_encoding_fuzz_corpus.cc b/cpp/src/parquet/arrow/generate_encoding_fuzz_corpus.cc index 1ff321c6abf3..5acf6097ef48 100644 --- a/cpp/src/parquet/arrow/generate_encoding_fuzz_corpus.cc +++ b/cpp/src/parquet/arrow/generate_encoding_fuzz_corpus.cc @@ -20,6 +20,7 @@ // More details about the format in fuzz_encoding_internal.h. #include +#include #include #include #include @@ -130,6 +131,64 @@ Result> SampleData() { } } + // Deliberately crafted value distributions that exercise encoder/decoder + // fast and slow paths: sorted runs (DELTA_BINARY_PACKED), constants (RLE + // runs), decimal fractions (ALP), and high-cardinality strings (dictionary + // encoding). + { + auto from_int64_values = + [](std::vector values) -> Result> { + ARROW_ASSIGN_OR_RAISE( + auto buffer, AllocateBuffer(static_cast(values.size()) * + static_cast(sizeof(int64_t)))); + std::memcpy(buffer->mutable_data(), values.data(), + values.size() * sizeof(int64_t)); + auto data = ArrayData::Make(int64(), static_cast(values.size()), + {nullptr, std::move(buffer)}, /*null_count=*/0); + return MakeArray(data); + }; + + std::vector ascending(1000); + std::vector descending(1000); + std::vector constant(1000, 123456789); + for (int64_t i = 0; i < 1000; ++i) { + ascending[i] = i; + descending[i] = -i * 7919; + } + ARROW_ASSIGN_OR_RAISE(auto ascending_arr, from_int64_values(std::move(ascending))); + push_array(std::move(ascending_arr)); + ARROW_ASSIGN_OR_RAISE(auto descending_arr, from_int64_values(std::move(descending))); + push_array(std::move(descending_arr)); + ARROW_ASSIGN_OR_RAISE(auto constant_arr, from_int64_values(std::move(constant))); + push_array(std::move(constant_arr)); + + // Decimal fractions with few significant digits are exactly representable + // and compress well with ALP. + std::vector alp(1000); + for (int64_t i = 0; i < 1000; ++i) { + alp[i] = (i % 1000) * 0.01; + } + ARROW_ASSIGN_OR_RAISE(auto alp_buffer, + AllocateBuffer(static_cast(alp.size()) * + static_cast(sizeof(double)))); + std::memcpy(alp_buffer->mutable_data(), alp.data(), alp.size() * sizeof(double)); + auto alp_data = + ArrayData::Make(float64(), static_cast(alp.size()), + {nullptr, std::move(alp_buffer)}, /*null_count=*/0); + push_array(MakeArray(alp_data)); + + // High-cardinality strings (contrast with BinaryWithRepeats above). + push_array(rag.String(500, /*min_length=*/8, /*max_length=*/16, + /*null_probability=*/0.0)); + } + + // Tiny pages, to exercise boundary paths in the encoders and decoders. + push_array(rag.Int32(1, /*min=*/0, /*max=*/10)); + push_array(rag.Int64(2, /*min=*/0, /*max=*/1'000)); + push_array(rag.Boolean(1, /*true_probability=*/0.5)); + push_array(rag.Float64(2, /*min=*/-1.0, /*max=*/1.0)); + push_array(rag.FixedSizeBinary(1, /*byte_width=*/3)); + return all_data; } @@ -166,10 +225,14 @@ Status DoMain(const std::string& out_dir) { auto write_sample = [&](Encoding::type source_encoding, Encoding::type roundtrip_encoding, FullParquetType type, - const BufferVector& buffers) -> Status { + const BufferVector& buffers, + const std::string& suffix = "") -> Status { std::stringstream ss; ss << type.ToString() << "-" << ::parquet::EncodingToString(source_encoding) << "-" << ::parquet::EncodingToString(roundtrip_encoding); + if (!suffix.empty()) { + ss << "-" << suffix; + } ARROW_ASSIGN_OR_RAISE(auto sample_fn, dir_fn.Join(sample_file_name(ss.str()))); std::cerr << sample_fn.ToString() << std::endl; ARROW_ASSIGN_OR_RAISE(auto file, io::FileOutputStream::Open(sample_fn.ToString())); @@ -203,6 +266,26 @@ Status DoMain(const std::string& out_dir) { write_sample(source_encoding, roundtrip_encoding, data.pq_type, buffers)); } } + + // Dictionary roundtrip variants: the payload stays PLAIN-encoded, but the + // header instructs the fuzzer to also roundtrip the decoded reference + // values through the dictionary encoders/decoders. + { + BufferVector buffers; + FuzzEncodingHeader header( + Encoding::PLAIN, Encoding::PLAIN, &descr, /*num_values=*/ + static_cast(data.values->length() - data.values->null_count()), + /*use_dictionary=*/true); + buffers.push_back(Buffer::FromString(header.Serialize())); + BEGIN_PARQUET_CATCH_EXCEPTIONS + auto encoder = ::parquet::MakeEncoder(data.pq_type.type, Encoding::PLAIN, + /*use_dictionary=*/false, &descr); + encoder->Put(*data.values); + buffers.push_back(encoder->FlushValues()); + END_PARQUET_CATCH_EXCEPTIONS + RETURN_NOT_OK(write_sample(Encoding::PLAIN, Encoding::PLAIN, data.pq_type, buffers, + /*suffix=*/"dict")); + } } // Special-case INT96 as there is no direct Arrow equivalent to encode. From 6a4a7924e7743a63d9e578b604d58e462ff88841 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Thu, 22 Oct 2026 08:35:59 +0100 Subject: [PATCH 2/2] OSS-Fuzz: Improve encoding fuzzer and its corpora Signed-off-by: Arthur Chan --- .../fuzzing/generate_corpuses.sh | 21 +++++++++++++++++++ .../parquet/arrow/fuzz_encoding_internal.cc | 20 ++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/cpp/build-support/fuzzing/generate_corpuses.sh b/cpp/build-support/fuzzing/generate_corpuses.sh index 2d95e110b516..e1c320165550 100755 --- a/cpp/build-support/fuzzing/generate_corpuses.sh +++ b/cpp/build-support/fuzzing/generate_corpuses.sh @@ -78,6 +78,15 @@ rm -rf ${CORPUS_DIR} "${OUT}/parquet-generate-encoding-fuzz-corpus" "${CORPUS_DIR}" "${ARROW_CPP}/build-support/fuzzing/pack_corpus.py" "${CORPUS_DIR}" "${OUT}/parquet-encoding-fuzz_seed_corpus.zip" +# Fuzz target dictionaries +# +# Dictionaries are shipped under build-support/fuzzing/dicts as +# ".dict" and copied next to the seed corpus zips, so that +# OSS-Fuzz's build script can pick them up like the corpora above. + +find "${ARROW_CPP}/build-support/fuzzing/dicts" -name '*.dict' \ + -exec cp '{}' "${OUT}/" \; + # CSV rm -rf "${PANDAS_DIR}" @@ -89,4 +98,16 @@ rm -rf "${CORPUS_DIR}" cp "${ARROW_ROOT}"/testing/data/csv/*.csv "${CORPUS_DIR}" # Add examples from Pandas test suite find "${PANDAS_DIR}/" -name "*.csv" -exec cp --backup=numbered '{}' "${CORPUS_DIR}" \; +# Some Pandas CSV files are exact duplicates of each other under different +# names, and pack_corpus.py fails on duplicate hashes; keep only one copy. +declare -A seen=() +while IFS= read -r -d '' f; do + h=$(sha1sum "$f" | cut -d' ' -f1) + if [[ -n "${seen[$h]}" ]]; then + rm -f "$f" + else + seen[$h]=1 + fi +done < <(find "${CORPUS_DIR}" -maxdepth 1 -type f -print0) + "${ARROW_CPP}/build-support/fuzzing/pack_corpus.py" "${CORPUS_DIR}" "${OUT}/arrow-csv-fuzz_seed_corpus.zip" diff --git a/cpp/src/parquet/arrow/fuzz_encoding_internal.cc b/cpp/src/parquet/arrow/fuzz_encoding_internal.cc index 926406cc6ae0..32e7afb21208 100644 --- a/cpp/src/parquet/arrow/fuzz_encoding_internal.cc +++ b/cpp/src/parquet/arrow/fuzz_encoding_internal.cc @@ -372,19 +372,21 @@ struct TypedFuzzEncoding { indices_buffer.data(), static_cast(indices_buffer.size())); ARROW_CHECK_GE(indices_size, 0); - // Decode the dictionary page with a PLAIN decoder, as a real reader does - auto dict_page_decoder = MakeDecoder(Encoding::PLAIN); - dict_page_decoder->SetData(dict_encoder->num_entries(), dict_buffer.data(), - dict_size); - - auto dict_decoder = MakeDictDecoder(descr_, pool()); - dict_decoder->SetDict(dict_page_decoder.get()); - dict_decoder->SetData(num_values_, indices_buffer.data(), indices_size); - + // Decode the dictionary page with a PLAIN decoder, as a real reader does. + // A fresh dictionary decoder must be prepared for each chunk size, like + // RunOnDecodedChunks() does, otherwise the decoder is exhausted after the + // first pass and subsequent passes fail with "Read less values than + // expected" even though the indices data is complete. auto compare_chunk = [&](int offset, std::span chunk_values) { return CompareChunkAgainstReference(offset, chunk_values); }; for (const int chunk_size : chunk_sizes()) { + auto dict_page_decoder = MakeDecoder(Encoding::PLAIN); + dict_page_decoder->SetData(dict_encoder->num_entries(), dict_buffer.data(), + dict_size); + auto dict_decoder = MakeDictDecoder(descr_, pool()); + dict_decoder->SetDict(dict_page_decoder.get()); + dict_decoder->SetData(num_values_, indices_buffer.data(), indices_size); RETURN_NOT_OK(RunOnPreparedDecoderChunks(dict_decoder.get(), chunk_size, compare_chunk)); }