@@ -7,16 +7,118 @@ use vortex_array::ToCanonical;
77use vortex_array:: arrays:: PrimitiveArray ;
88use vortex_array:: builders:: { ArrayBuilder , PrimitiveBuilder , UninitRange } ;
99use vortex_array:: patches:: Patches ;
10+ use vortex_array:: validity:: Validity ;
11+ use vortex_array:: vtable:: ValidityHelper ;
12+ use vortex_buffer:: BufferMut ;
1013use vortex_dtype:: {
11- IntegerPType , NativePType , match_each_integer_ptype, match_each_unsigned_integer_ptype,
14+ IntegerPType , NativePType , UnsignedPType , match_each_integer_ptype,
15+ match_each_unsigned_integer_ptype,
1216} ;
1317use vortex_error:: { VortexExpect , vortex_panic} ;
14- use vortex_mask:: Mask ;
18+ use vortex_mask:: { Mask , MaskMut } ;
1519use vortex_scalar:: Scalar ;
20+ use vortex_vector:: primitive:: { PVectorMut , PrimitiveVectorMut } ;
1621
1722use crate :: BitPackedArray ;
1823use crate :: unpack_iter:: BitPacked ;
1924
25+ /// Unpacks a bit-packed array into a primitive vector.
26+ pub fn unpack_to_primitive_vector ( array : & BitPackedArray ) -> PrimitiveVectorMut {
27+ match_each_integer_ptype ! ( array. ptype( ) , |P | { unpack_to_pvector:: <P >( array) . into( ) } )
28+ }
29+
30+ /// Unpacks a bit-packed array into a generic [`PVectorMut`].
31+ pub fn unpack_to_pvector < P : BitPacked > ( array : & BitPackedArray ) -> PVectorMut < P > {
32+ if array. is_empty ( ) {
33+ return PVectorMut :: with_capacity ( 0 ) ;
34+ }
35+
36+ let len = array. len ( ) ;
37+ let mut elements = BufferMut :: < P > :: with_capacity ( len) ;
38+ let uninit_slice = & mut elements. spare_capacity_mut ( ) [ ..len] ;
39+ debug_assert_eq ! ( uninit_slice. len( ) , len) ;
40+
41+ // Decode into an uninitialized slice.
42+ let mut bit_packed_iter = array. unpacked_chunks ( ) ;
43+ bit_packed_iter. decode_into ( uninit_slice) ;
44+ // SAFETY: `decode_into` initialized exactly `len` elements into the spare (existing) capacity.
45+ unsafe { elements. set_len ( len) } ;
46+
47+ let mut validity = array. validity_mask ( ) . into_mut ( ) ;
48+ debug_assert_eq ! ( validity. len( ) , len) ;
49+
50+ // TODO(connor): Implement a fused version of patching instead.
51+ if let Some ( patches) = array. patches ( ) {
52+ let patch_indices = patches. indices ( ) . to_primitive ( ) ;
53+ let patch_values = patches. values ( ) . to_primitive ( ) ;
54+ let patches_validity = patch_values. validity ( ) ;
55+ let patch_offset = patches. offset ( ) ;
56+
57+ let patch_values_slice = patch_values. as_slice :: < P > ( ) ;
58+ match_each_unsigned_integer_ptype ! ( patch_indices. ptype( ) , |I | {
59+ let patch_indices_slice = patch_indices. as_slice:: <I >( ) ;
60+
61+ // SAFETY:
62+ // - `Patches` invariant guarantees indices are sorted and within array bounds.
63+ // - `patch_indices` and `patch_values` have equal length (from `Patches` invariant).
64+ // - `elements` and `validity` have equal length (both are `len` from the array).
65+ // - All patch indices are valid after offset adjustment (guaranteed by `Patches`).
66+ unsafe {
67+ apply_patches_inner(
68+ & mut elements,
69+ & mut validity,
70+ patch_indices_slice,
71+ patch_offset,
72+ patch_values_slice,
73+ patches_validity,
74+ )
75+ } ;
76+ } ) ;
77+ }
78+
79+ // SAFETY: `elements` and `validity` have the same length.
80+ unsafe { PVectorMut :: new_unchecked ( elements, validity) }
81+ }
82+
83+ /// # Safety
84+ ///
85+ /// - All indices in `patch_indices` after subtracting `patch_offset` must be valid indices
86+ /// into both `buffer` and `validity`.
87+ /// - `patch_indices` must be sorted in ascending order.
88+ /// - `patch_indices` and `patch_values` must have the same length.
89+ /// - `buffer` and `validity` must have the same length.
90+ unsafe fn apply_patches_inner < P , I > (
91+ buffer : & mut [ P ] ,
92+ validity : & mut MaskMut ,
93+ patch_indices : & [ I ] ,
94+ patch_offset : usize ,
95+ patch_values : & [ P ] ,
96+ patches_validity : & Validity ,
97+ ) where
98+ P : NativePType ,
99+ I : UnsignedPType ,
100+ {
101+ debug_assert ! ( !patch_indices. is_empty( ) ) ;
102+ debug_assert_eq ! ( patch_indices. len( ) , patch_values. len( ) ) ;
103+ debug_assert_eq ! ( buffer. len( ) , validity. len( ) ) ;
104+ debug_assert ! ( patch_indices. is_sorted( ) ) ;
105+ debug_assert ! ( patch_indices. last( ) . vortex_expect( "can't be empty" ) . as_( ) <= validity. len( ) ) ;
106+
107+ match patches_validity {
108+ Validity :: NonNullable | Validity :: AllValid => {
109+ for ( & i, & value) in patch_indices. iter ( ) . zip_eq ( patch_values) {
110+ let index = i. as_ ( ) - patch_offset;
111+
112+ // SAFETY: `index` is valid because caller guarantees all patch indices are within
113+ // bounds after offset adjustment.
114+ unsafe { validity. set_unchecked ( index) } ;
115+ buffer[ index] = value;
116+ }
117+ }
118+ _ => vortex_panic ! ( "BitPackedArray somehow had nullable patch values" ) ,
119+ }
120+ }
121+
20122pub fn unpack_array ( array : & BitPackedArray ) -> PrimitiveArray {
21123 match_each_integer_ptype ! ( array. ptype( ) , |P | { unpack_primitive_array:: <P >( array) } )
22124}
0 commit comments