11// SPDX-License-Identifier: Apache-2.0
22// SPDX-FileCopyrightText: Copyright the Vortex contributors
33
4+ use std:: sync:: Arc ;
45use std:: sync:: LazyLock ;
56
67use parking_lot:: Mutex ;
@@ -10,11 +11,16 @@ use vortex_array::Canonical;
1011use vortex_array:: ExecutionCtx ;
1112use vortex_array:: IntoArray ;
1213use vortex_array:: VortexSessionExecute ;
14+ use vortex_array:: aggregate_fn:: fns:: uncompressed_size_in_bytes:: UncompressedSizeOpts ;
15+ use vortex_array:: aggregate_fn:: fns:: uncompressed_size_in_bytes:: uncompressed_size_in_bytes;
1316use vortex_array:: arrays:: BoolArray ;
1417use vortex_array:: arrays:: Constant ;
1518use vortex_array:: arrays:: Map ;
1619use vortex_array:: arrays:: NullArray ;
1720use vortex_array:: arrays:: PrimitiveArray ;
21+ use vortex_array:: arrays:: VarBinView ;
22+ use vortex_array:: arrays:: VarBinViewArray ;
23+ use vortex_array:: arrays:: varbinview:: BinaryView ;
1824use vortex_array:: assert_arrays_eq;
1925use vortex_array:: builders:: MapBuilder ;
2026use vortex_array:: dtype:: DType ;
@@ -23,6 +29,8 @@ use vortex_array::dtype::Nullability;
2329use vortex_array:: dtype:: PType ;
2430use vortex_array:: scalar:: Scalar ;
2531use vortex_array:: validity:: Validity ;
32+ use vortex_buffer:: Buffer ;
33+ use vortex_buffer:: ByteBuffer ;
2634use vortex_buffer:: buffer;
2735use vortex_error:: VortexResult ;
2836use vortex_session:: VortexSession ;
@@ -845,3 +853,48 @@ fn map_compression_preserves_repeated_entry_children() -> VortexResult<()> {
845853 assert_arrays_eq ! ( & compressed, & array, & mut exec_ctx) ;
846854 Ok ( ( ) )
847855}
856+
857+ /// Distinct 27-byte strings whose views reference a small prefix of one 2 MiB buffer, the way
858+ /// Arrow's Parquet reader shares a decompressed page between batches.
859+ fn views_into_shared_page ( rows : u32 ) -> ArrayRef {
860+ const VALUE_LEN : u32 = 27 ;
861+ let mut page = vec ! [ 0u8 ; 2 << 20 ] ;
862+ for row in 0 ..rows {
863+ let value = format ! ( "value-{row:021}" ) ;
864+ let start = ( row * VALUE_LEN ) as usize ;
865+ page[ start..start + VALUE_LEN as usize ] . copy_from_slice ( value. as_bytes ( ) ) ;
866+ }
867+ let page = ByteBuffer :: from ( page) ;
868+ let views = ( 0 ..rows)
869+ . map ( |row| {
870+ let start = ( row * VALUE_LEN ) as usize ;
871+ BinaryView :: make_view ( & page[ start..start + VALUE_LEN as usize ] , 0 , row * VALUE_LEN )
872+ } )
873+ . collect :: < Buffer < BinaryView > > ( ) ;
874+ // SAFETY: every view points at `VALUE_LEN` bytes inside the page.
875+ unsafe {
876+ VarBinViewArray :: new_unchecked (
877+ views,
878+ Arc :: from ( [ page] ) ,
879+ DType :: Utf8 ( Nullability :: NonNullable ) ,
880+ Validity :: NonNullable ,
881+ )
882+ }
883+ . into_array ( )
884+ }
885+
886+ #[ test]
887+ fn scheme_must_beat_referenced_bytes_not_held_bytes ( ) -> VortexResult < ( ) > {
888+ let mut exec_ctx = SESSION . create_execution_ctx ( ) ;
889+ let array = views_into_shared_page ( 2000 ) ;
890+ let exact = uncompressed_size_in_bytes ( & array, UncompressedSizeOpts :: exact ( ) , & mut exec_ctx) ?;
891+
892+ // Every value is distinct, so a dictionary is larger than the referenced bytes but far
893+ // smaller than the 2 MiB the array holds. It must lose.
894+ let compressed = compressor ( ) . compress ( & array, & mut exec_ctx) ?;
895+
896+ assert ! ( compressed. is:: <VarBinView >( ) ) ;
897+ assert_eq ! ( compressed. nbytes( ) , exact as u64 ) ;
898+ assert_arrays_eq ! ( & compressed, & array, & mut exec_ctx) ;
899+ Ok ( ( ) )
900+ }
0 commit comments