@@ -30,9 +30,15 @@ use vortex_array::ExecutionCtx;
3030use vortex_array:: accessor:: ArrayAccessor ;
3131use vortex_array:: arrays:: BoolArray ;
3232use vortex_array:: arrays:: DecimalArray ;
33+ use vortex_array:: arrays:: ExtensionArray ;
34+ use vortex_array:: arrays:: FixedSizeListArray ;
3335use vortex_array:: arrays:: NullArray ;
3436use vortex_array:: arrays:: PrimitiveArray ;
37+ use vortex_array:: arrays:: StructArray ;
3538use vortex_array:: arrays:: VarBinViewArray ;
39+ use vortex_array:: arrays:: extension:: ExtensionArrayExt ;
40+ use vortex_array:: arrays:: fixed_size_list:: FixedSizeListArrayExt ;
41+ use vortex_array:: arrays:: struct_:: StructArrayExt ;
3642use vortex_array:: dtype:: DType ;
3743use vortex_array:: dtype:: DecimalType ;
3844use vortex_array:: dtype:: NativePType ;
@@ -112,9 +118,28 @@ pub fn row_width_for_dtype(dtype: &DType) -> VortexResult<RowWidth> {
112118 ) ) )
113119 }
114120 DType :: Utf8 ( _) | DType :: Binary ( _) => Ok ( RowWidth :: Variable ) ,
115- DType :: Struct ( ..) | DType :: FixedSizeList ( ..) | DType :: List ( ..) | DType :: Extension ( ..) => {
116- vortex_bail ! ( "row encoding for {} is not yet supported" , dtype)
121+ DType :: FixedSizeList ( elem, n, _) => match row_width_for_dtype ( elem) ? {
122+ // FSL is fixed iff its element type is fixed. Add a sentinel byte for the FSL
123+ // itself, then `n` copies of the element width.
124+ RowWidth :: Fixed ( w) => {
125+ let body = w. saturating_mul ( * n) ;
126+ Ok ( RowWidth :: Fixed ( body. saturating_add ( 1 ) ) )
127+ }
128+ RowWidth :: Variable => Ok ( RowWidth :: Variable ) ,
129+ } ,
130+ DType :: Struct ( fields, _) => {
131+ // Struct is fixed iff all its fields are fixed; sum their widths plus a sentinel.
132+ let mut total: u32 = 1 ; // outer sentinel
133+ for field_dtype in fields. fields ( ) {
134+ match row_width_for_dtype ( & field_dtype) ? {
135+ RowWidth :: Fixed ( w) => total = total. saturating_add ( w) ,
136+ RowWidth :: Variable => return Ok ( RowWidth :: Variable ) ,
137+ }
138+ }
139+ Ok ( RowWidth :: Fixed ( total) )
117140 }
141+ DType :: List ( ..) => Ok ( RowWidth :: Variable ) ,
142+ DType :: Extension ( ext) => row_width_for_dtype ( ext. storage_dtype ( ) ) ,
118143 DType :: Variant ( _) => {
119144 vortex_bail ! ( "row encoding does not support Variant arrays (no well-defined ordering)" )
120145 }
@@ -133,7 +158,7 @@ pub fn row_width_for_dtype(dtype: &DType) -> VortexResult<RowWidth> {
133158/// variants land in later commits.
134159pub fn field_size (
135160 canonical : & Canonical ,
136- _field : SortField ,
161+ field : SortField ,
137162 sizes : & mut [ u32 ] ,
138163 ctx : & mut ExecutionCtx ,
139164) -> VortexResult < ( ) > {
@@ -143,10 +168,10 @@ pub fn field_size(
143168 Canonical :: Primitive ( arr) => add_size_primitive ( arr, sizes) ,
144169 Canonical :: Decimal ( arr) => add_size_decimal ( arr, sizes) ,
145170 Canonical :: VarBinView ( arr) => add_size_varbinview ( arr, sizes, ctx) ?,
146- Canonical :: Struct ( _ )
147- | Canonical :: FixedSizeList ( _ )
148- | Canonical :: Extension ( _ )
149- | Canonical :: List ( _) => vortex_bail ! (
171+ Canonical :: Struct ( arr ) => add_size_struct ( arr , field , sizes , ctx ) ? ,
172+ Canonical :: FixedSizeList ( arr ) => add_size_fsl ( arr , field , sizes , ctx ) ? ,
173+ Canonical :: Extension ( arr ) => add_size_extension ( arr , field , sizes , ctx ) ? ,
174+ Canonical :: List ( _) => vortex_bail ! (
150175 "row encoding does not yet support canonical type {:?}" ,
151176 canonical. dtype( )
152177 ) ,
@@ -177,10 +202,10 @@ pub fn field_encode(
177202 Canonical :: Primitive ( arr) => encode_primitive ( arr, field, offsets, cursors, out, ctx) ?,
178203 Canonical :: Decimal ( arr) => encode_decimal ( arr, field, offsets, cursors, out, ctx) ?,
179204 Canonical :: VarBinView ( arr) => encode_varbinview ( arr, field, offsets, cursors, out, ctx) ?,
180- Canonical :: Struct ( _ )
181- | Canonical :: FixedSizeList ( _ )
182- | Canonical :: Extension ( _ )
183- | Canonical :: List ( _) => vortex_bail ! (
205+ Canonical :: Struct ( arr ) => encode_struct ( arr , field , offsets , cursors , out , ctx ) ? ,
206+ Canonical :: FixedSizeList ( arr ) => encode_fsl ( arr , field , offsets , cursors , out , ctx ) ? ,
207+ Canonical :: Extension ( arr ) => encode_extension ( arr , field , offsets , cursors , out , ctx ) ? ,
208+ Canonical :: List ( _) => vortex_bail ! (
184209 "row encoding does not yet support canonical type {:?}" ,
185210 canonical. dtype( )
186211 ) ,
@@ -234,6 +259,60 @@ fn add_size_varbinview(
234259 Ok ( ( ) )
235260}
236261
262+ fn add_size_struct (
263+ arr : & StructArray ,
264+ field : SortField ,
265+ sizes : & mut [ u32 ] ,
266+ ctx : & mut ExecutionCtx ,
267+ ) -> VortexResult < ( ) > {
268+ // null sentinel: 1 byte per row.
269+ for s in sizes. iter_mut ( ) {
270+ * s += 1 ;
271+ }
272+ // Each field adds its own per-row size.
273+ for child in arr. iter_unmasked_fields ( ) {
274+ let canonical = child. clone ( ) . execute :: < Canonical > ( ctx) ?;
275+ field_size ( & canonical, field, sizes, ctx) ?;
276+ }
277+ Ok ( ( ) )
278+ }
279+
280+ fn add_size_fsl (
281+ arr : & FixedSizeListArray ,
282+ field : SortField ,
283+ sizes : & mut [ u32 ] ,
284+ ctx : & mut ExecutionCtx ,
285+ ) -> VortexResult < ( ) > {
286+ let n = arr. len ( ) ;
287+ debug_assert_eq ! ( n, sizes. len( ) ) ;
288+ let list_size = arr. list_size ( ) as usize ;
289+ let elements = arr. elements ( ) . clone ( ) . execute :: < Canonical > ( ctx) ?;
290+ debug_assert_eq ! ( elements. len( ) , n * list_size) ;
291+ // Sizing: 1 sentinel + sum of element sizes (`list_size` per row).
292+ // We compute element-wise sizes into a contiguous scratch buffer then reduce by row.
293+ let mut elem_sizes = vec ! [ 0u32 ; n * list_size] ;
294+ field_size ( & elements, field, & mut elem_sizes, ctx) ?;
295+ for i in 0 ..n {
296+ let mut sum: u32 = 1 ; // sentinel
297+ let base = i * list_size;
298+ for j in 0 ..list_size {
299+ sum = sum. saturating_add ( elem_sizes[ base + j] ) ;
300+ }
301+ sizes[ i] += sum;
302+ }
303+ Ok ( ( ) )
304+ }
305+
306+ fn add_size_extension (
307+ arr : & ExtensionArray ,
308+ field : SortField ,
309+ sizes : & mut [ u32 ] ,
310+ ctx : & mut ExecutionCtx ,
311+ ) -> VortexResult < ( ) > {
312+ let storage = arr. storage_array ( ) . clone ( ) . execute :: < Canonical > ( ctx) ?;
313+ field_size ( & storage, field, sizes, ctx)
314+ }
315+
237316fn encode_null (
238317 arr : & NullArray ,
239318 field : SortField ,
@@ -413,6 +492,132 @@ fn encode_varbinview(
413492 Ok ( ( ) )
414493}
415494
495+ fn encode_struct (
496+ arr : & StructArray ,
497+ field : SortField ,
498+ row_offsets : & [ u32 ] ,
499+ col_offset : & mut [ u32 ] ,
500+ out : & mut [ u8 ] ,
501+ ctx : & mut ExecutionCtx ,
502+ ) -> VortexResult < ( ) > {
503+ let n = arr. len ( ) ;
504+ let mask = arr. as_ref ( ) . validity ( ) ?. execute_mask ( n, ctx) ?;
505+ let non_null = field. non_null_sentinel ( ) ;
506+ let null = field. null_sentinel ( ) ;
507+
508+ // First, write the sentinel for each row. We track the post-sentinel cursor offsets
509+ // for the body in `body_cursors` (which start exactly at +1 of the input cursor).
510+ // For null rows we additionally need to zero-fill the (uniform-width) field bytes,
511+ // but because struct widths are variable in general, we record null indexes first
512+ // and zero-fill after we know each row's contribution.
513+ //
514+ // To keep the implementation simple we:
515+ // 1) advance the cursor past the sentinel,
516+ // 2) recursively encode each field's bytes (the field encoders ignore nullness of
517+ // the struct, but use their own per-field nullness),
518+ // 3) for null struct rows, overwrite the body bytes with zeros so the encoded form
519+ // depends only on the sentinel.
520+ let body_start: Vec < u32 > = ( 0 ..n) . map ( |i| col_offset[ i] + 1 ) . collect ( ) ;
521+ for i in 0 ..n {
522+ let pos = ( row_offsets[ i] + col_offset[ i] ) as usize ;
523+ out[ pos] = if mask. value ( i) { non_null } else { null } ;
524+ col_offset[ i] += 1 ;
525+ }
526+
527+ for child in arr. iter_unmasked_fields ( ) {
528+ let canonical = child. clone ( ) . execute :: < Canonical > ( ctx) ?;
529+ field_encode ( & canonical, field, row_offsets, col_offset, out, ctx) ?;
530+ }
531+
532+ // Zero-fill body bytes of null rows (the field encoders may have written values).
533+ for i in 0 ..n {
534+ if !mask. value ( i) {
535+ let start = ( row_offsets[ i] + body_start[ i] ) as usize ;
536+ let end = ( row_offsets[ i] + col_offset[ i] ) as usize ;
537+ for b in & mut out[ start..end] {
538+ * b = 0 ;
539+ }
540+ }
541+ }
542+
543+ Ok ( ( ) )
544+ }
545+
546+ fn encode_fsl (
547+ arr : & FixedSizeListArray ,
548+ field : SortField ,
549+ row_offsets : & [ u32 ] ,
550+ col_offset : & mut [ u32 ] ,
551+ out : & mut [ u8 ] ,
552+ ctx : & mut ExecutionCtx ,
553+ ) -> VortexResult < ( ) > {
554+ let n = arr. len ( ) ;
555+ let list_size = arr. list_size ( ) as usize ;
556+ let mask = arr. as_ref ( ) . validity ( ) ?. execute_mask ( n, ctx) ?;
557+ let non_null = field. non_null_sentinel ( ) ;
558+ let null = field. null_sentinel ( ) ;
559+ let elements = arr. elements ( ) . clone ( ) . execute :: < Canonical > ( ctx) ?;
560+ debug_assert_eq ! ( elements. len( ) , n * list_size) ;
561+
562+ // Write sentinels and remember body start for null zero-fill.
563+ let body_start: Vec < u32 > = ( 0 ..n) . map ( |i| col_offset[ i] + 1 ) . collect ( ) ;
564+ for i in 0 ..n {
565+ let pos = ( row_offsets[ i] + col_offset[ i] ) as usize ;
566+ out[ pos] = if mask. value ( i) { non_null } else { null } ;
567+ col_offset[ i] += 1 ;
568+ }
569+
570+ // Encode all `n * list_size` elements into the body. Build a fresh
571+ // (offsets, cursors) pair where each element gets one slot. Then sum bytes back
572+ // into the parent col_offset.
573+ let mut elem_sizes = vec ! [ 0u32 ; n * list_size] ;
574+ field_size ( & elements, field, & mut elem_sizes, ctx) ?;
575+ // Element offsets are sequential starting at each parent's current cursor position.
576+ let mut elem_offsets = vec ! [ 0u32 ; n * list_size] ;
577+ for i in 0 ..n {
578+ let mut acc = row_offsets[ i] + col_offset[ i] ;
579+ for j in 0 ..list_size {
580+ elem_offsets[ i * list_size + j] = acc;
581+ acc = acc. saturating_add ( elem_sizes[ i * list_size + j] ) ;
582+ }
583+ }
584+ let mut elem_cursors = vec ! [ 0u32 ; n * list_size] ;
585+ field_encode ( & elements, field, & elem_offsets, & mut elem_cursors, out, ctx) ?;
586+ // Advance the parent cursors by the total per-row element bytes.
587+ for i in 0 ..n {
588+ let mut sum: u32 = 0 ;
589+ for j in 0 ..list_size {
590+ sum = sum. saturating_add ( elem_sizes[ i * list_size + j] ) ;
591+ }
592+ col_offset[ i] = col_offset[ i] . saturating_add ( sum) ;
593+ }
594+
595+ // Zero-fill null bodies.
596+ for i in 0 ..n {
597+ if !mask. value ( i) {
598+ let start = ( row_offsets[ i] + body_start[ i] ) as usize ;
599+ let end = ( row_offsets[ i] + col_offset[ i] ) as usize ;
600+ for b in & mut out[ start..end] {
601+ * b = 0 ;
602+ }
603+ }
604+ }
605+
606+ Ok ( ( ) )
607+ }
608+
609+ fn encode_extension (
610+ arr : & ExtensionArray ,
611+ field : SortField ,
612+ row_offsets : & [ u32 ] ,
613+ col_offset : & mut [ u32 ] ,
614+ out : & mut [ u8 ] ,
615+ ctx : & mut ExecutionCtx ,
616+ ) -> VortexResult < ( ) > {
617+ let storage = arr. storage_array ( ) . clone ( ) . execute :: < Canonical > ( ctx) ?;
618+ field_encode ( & storage, field, row_offsets, col_offset, out, ctx)
619+ }
620+
416621/// Encode a variable-length byte slice into `out` in 32-byte blocks with
417622/// continuation markers. Returns the number of bytes written.
418623fn encode_varlen_value ( bytes : & [ u8 ] , out : & mut [ u8 ] , descending : bool ) -> u32 {
0 commit comments