@@ -274,6 +274,13 @@ TEST(ParquetUtils, SliceValueBytesIntoRawBytes_FixedSizeMisaligned) {
274274 InvalidInputException);
275275}
276276
277+ TEST (ParquetUtils, SliceValueBytesIntoRawBytes_UnsupportedFormat) {
278+ std::vector<uint8_t > bytes = {0x01 ,0x00 ,0x00 ,0x00 };
279+ EXPECT_THROW (
280+ SliceValueBytesIntoRawBytes (bytes, Type::INT32 , std::nullopt , Format::RLE ),
281+ DBPSUnsupportedException);
282+ }
283+
277284TEST (ParquetUtils, CombineRawBytesIntoValueBytes_INT32) {
278285 std::vector<RawValueBytes> elems = {
279286 {0x04 ,0x03 ,0x02 ,0x01 },
@@ -309,6 +316,15 @@ TEST(ParquetUtils, CombineRawBytesIntoValueBytes_FIXED_LEN_BYTE_ARRAY_SizeMismat
309316 InvalidInputException);
310317}
311318
319+ TEST (ParquetUtils, CombineRawBytesIntoValueBytes_UnsupportedFormat) {
320+ std::vector<RawValueBytes> elems = {
321+ {0x04 ,0x03 ,0x02 ,0x01 }
322+ };
323+ EXPECT_THROW (
324+ CombineRawBytesIntoValueBytes (elems, Type::INT32 , std::nullopt , Format::RLE ),
325+ DBPSUnsupportedException);
326+ }
327+
312328TEST (ParquetUtils, SliceAndCombine_RoundTrip_INT64) {
313329 // Two int64 values: little-endian bytes
314330 std::vector<uint8_t > bytes = {
@@ -402,3 +418,149 @@ TEST(ParquetUtils, DecompressAndSplit_DataPageV2_UnsupportedCompression) {
402418 DecompressAndSplit (plaintext, CompressionCodec::LZO , attribs),
403419 DBPSUnsupportedException);
404420}
421+
422+ TEST (ParquetUtils, CompressAndJoin_DataPageV1_Compressed) {
423+ AttributesMap attribs = {
424+ {" page_type" , std::string (" DATA_PAGE_V1" )},
425+ {" data_page_num_values" , int32_t (2 )},
426+ {" data_page_max_repetition_level" , int32_t (0 )},
427+ {" data_page_max_definition_level" , int32_t (1 )},
428+ {" page_v1_repetition_level_encoding" , std::string (" RLE" )},
429+ {" page_v1_definition_level_encoding" , std::string (" RLE" )}
430+ };
431+
432+ std::vector<uint8_t > level_bytes;
433+ append_u32_le (level_bytes, 2 ); // RLE block length
434+ level_bytes.insert (level_bytes.end (), {0x0A , 0x0B });
435+ std::vector<uint8_t > value_bytes = {0x21 , 0x22 , 0x23 , 0x24 };
436+
437+ auto joined = CompressAndJoin (level_bytes, value_bytes, CompressionCodec::SNAPPY , attribs);
438+ auto expected = Compress (Join (level_bytes, value_bytes), CompressionCodec::SNAPPY );
439+ EXPECT_EQ (joined, expected);
440+
441+ auto decomposed = DecompressAndSplit (joined, CompressionCodec::SNAPPY , attribs);
442+ EXPECT_EQ (decomposed.level_bytes , level_bytes);
443+ EXPECT_EQ (decomposed.value_bytes , value_bytes);
444+ }
445+
446+ TEST (ParquetUtils, CompressAndJoin_DataPageV2_Uncompressed) {
447+ AttributesMap attribs = {
448+ {" page_type" , std::string (" DATA_PAGE_V2" )},
449+ {" data_page_num_values" , int32_t (3 )},
450+ {" data_page_max_definition_level" , int32_t (1 )},
451+ {" data_page_max_repetition_level" , int32_t (0 )},
452+ {" page_v2_definition_levels_byte_length" , int32_t (2 )},
453+ {" page_v2_repetition_levels_byte_length" , int32_t (1 )},
454+ {" page_v2_num_nulls" , int32_t (0 )},
455+ {" page_v2_is_compressed" , false }
456+ };
457+
458+ std::vector<uint8_t > level_bytes = {0x10 , 0x11 , 0x12 }; // 2+1
459+ std::vector<uint8_t > value_bytes = {0x21 , 0x22 , 0x23 , 0x24 };
460+
461+ auto joined = CompressAndJoin (level_bytes, value_bytes, CompressionCodec::UNCOMPRESSED , attribs);
462+ std::vector<uint8_t > expected = Join (level_bytes, value_bytes);
463+ EXPECT_EQ (joined, expected);
464+
465+ auto decomposed = DecompressAndSplit (joined, CompressionCodec::UNCOMPRESSED , attribs);
466+ EXPECT_EQ (decomposed.level_bytes , level_bytes);
467+ EXPECT_EQ (decomposed.value_bytes , value_bytes);
468+ }
469+
470+ TEST (ParquetUtils, CompressAndJoin_DataPageV2_Compressed_RoundTrip) {
471+ AttributesMap attribs = {
472+ {" page_type" , std::string (" DATA_PAGE_V2" )},
473+ {" data_page_num_values" , int32_t (4 )},
474+ {" data_page_max_definition_level" , int32_t (1 )},
475+ {" data_page_max_repetition_level" , int32_t (0 )},
476+ {" page_v2_definition_levels_byte_length" , int32_t (2 )},
477+ {" page_v2_repetition_levels_byte_length" , int32_t (1 )},
478+ {" page_v2_num_nulls" , int32_t (0 )},
479+ {" page_v2_is_compressed" , true }
480+ };
481+
482+ std::vector<uint8_t > level_bytes = {0x10 , 0x11 , 0x12 }; // len matches 2+1
483+ std::vector<uint8_t > value_bytes = {0x21 , 0x22 , 0x23 , 0x24 , 0x25 };
484+
485+ auto joined = CompressAndJoin (level_bytes, value_bytes, CompressionCodec::SNAPPY , attribs);
486+ auto expected_compressed = Compress (value_bytes, CompressionCodec::SNAPPY );
487+ auto expected_joined = Join (level_bytes, expected_compressed);
488+ EXPECT_EQ (joined, expected_joined);
489+
490+ auto decomposed = DecompressAndSplit (joined, CompressionCodec::SNAPPY , attribs);
491+
492+ EXPECT_EQ (decomposed.level_bytes , level_bytes);
493+ EXPECT_EQ (decomposed.value_bytes , value_bytes);
494+ }
495+
496+ TEST (ParquetUtils, CompressAndJoin_DataPageV2_LevelLengthMismatch) {
497+ AttributesMap attribs = {
498+ {" page_type" , std::string (" DATA_PAGE_V2" )},
499+ {" data_page_num_values" , int32_t (2 )},
500+ {" data_page_max_definition_level" , int32_t (1 )},
501+ {" data_page_max_repetition_level" , int32_t (0 )},
502+ {" page_v2_definition_levels_byte_length" , int32_t (2 )},
503+ {" page_v2_repetition_levels_byte_length" , int32_t (1 )},
504+ {" page_v2_num_nulls" , int32_t (0 )},
505+ {" page_v2_is_compressed" , true }
506+ };
507+
508+ std::vector<uint8_t > level_bytes = {0x10 , 0x11 }; // expected 3 bytes -> mismatch
509+ std::vector<uint8_t > value_bytes = {0x21 , 0x22 , 0x23 };
510+
511+ EXPECT_THROW (
512+ CompressAndJoin (level_bytes, value_bytes, CompressionCodec::SNAPPY , attribs),
513+ InvalidInputException);
514+ }
515+
516+ TEST (ParquetUtils, CompressAndJoin_DictionaryPage) {
517+ AttributesMap attribs = {
518+ {" page_type" , std::string (" DICTIONARY_PAGE" )}
519+ };
520+
521+ std::vector<uint8_t > level_bytes; // must be empty
522+ std::vector<uint8_t > value_bytes = {0x31 , 0x32 , 0x33 };
523+
524+ auto joined = CompressAndJoin (level_bytes, value_bytes, CompressionCodec::UNCOMPRESSED , attribs);
525+ EXPECT_EQ (joined, value_bytes);
526+ }
527+
528+ TEST (ParquetUtils, CompressAndJoin_UnsupportedCompression) {
529+ AttributesMap attribs = {
530+ {" page_type" , std::string (" DATA_PAGE_V1" )},
531+ {" data_page_num_values" , int32_t (1 )},
532+ {" data_page_max_repetition_level" , int32_t (0 )},
533+ {" data_page_max_definition_level" , int32_t (0 )},
534+ {" page_v1_repetition_level_encoding" , std::string (" RLE" )},
535+ {" page_v1_definition_level_encoding" , std::string (" RLE" )}
536+ };
537+
538+ std::vector<uint8_t > level_bytes;
539+ std::vector<uint8_t > value_bytes = {0x40 };
540+
541+ EXPECT_THROW (
542+ CompressAndJoin (level_bytes, value_bytes, CompressionCodec::LZO , attribs),
543+ DBPSUnsupportedException);
544+ }
545+
546+ TEST (ParquetUtils, CompressAndJoin_UnsupportedEncoding) {
547+ AttributesMap attribs = {
548+ {" page_type" , std::string (" DATA_PAGE_V1" )},
549+ {" data_page_num_values" , int32_t (1 )},
550+ {" data_page_max_repetition_level" , int32_t (1 )}, // triggers repetition level parsing
551+ {" data_page_max_definition_level" , int32_t (0 )},
552+ {" page_v1_repetition_level_encoding" , std::string (" BIT_PACKED" )}, // unsupported
553+ {" page_v1_definition_level_encoding" , std::string (" RLE" )}
554+ };
555+
556+ // Build minimal level bytes that would be valid if encoding were RLE: len + payload
557+ std::vector<uint8_t > level_bytes;
558+ append_u32_le (level_bytes, 1 );
559+ level_bytes.push_back (0x00 );
560+
561+ std::vector<uint8_t > value_bytes = {0x01 , 0x02 };
562+
563+ EXPECT_THROW (
564+ CompressAndJoin (level_bytes, value_bytes, CompressionCodec::UNCOMPRESSED , attribs),
565+ InvalidInputException);
566+ }
0 commit comments