@@ -65,7 +65,7 @@ enum TypedArray<'a> {
6565 LargeString ( & ' a LargeStringArray ) ,
6666 Binary ( & ' a BinaryArray ) ,
6767 LargeBinary ( & ' a LargeBinaryArray ) ,
68- Struct ( & ' a StructArray , arrow:: datatypes:: Fields ) ,
68+ Struct ( & ' a StructArray , arrow:: datatypes:: Fields , Vec < TypedElements < ' a > > ) ,
6969 List ( & ' a ListArray , arrow:: datatypes:: FieldRef ) ,
7070 LargeList ( & ' a LargeListArray , arrow:: datatypes:: FieldRef ) ,
7171 Map ( & ' a MapArray , arrow:: datatypes:: FieldRef ) ,
@@ -162,12 +162,20 @@ impl<'a> TypedArray<'a> {
162162 CometError :: Internal ( "Failed to downcast to LargeBinaryArray" . to_string ( ) )
163163 } ) ?,
164164 ) ) ,
165- DataType :: Struct ( fields) => Ok ( TypedArray :: Struct (
166- array. as_any ( ) . downcast_ref :: < StructArray > ( ) . ok_or_else ( || {
165+ DataType :: Struct ( fields) => {
166+ let struct_arr = array. as_any ( ) . downcast_ref :: < StructArray > ( ) . ok_or_else ( || {
167167 CometError :: Internal ( "Failed to downcast to StructArray" . to_string ( ) )
168- } ) ?,
169- fields. clone ( ) ,
170- ) ) ,
168+ } ) ?;
169+ // Pre-downcast all struct fields once
170+ let typed_fields: Vec < TypedElements > = fields
171+ . iter ( )
172+ . enumerate ( )
173+ . map ( |( idx, field) | {
174+ TypedElements :: from_array ( struct_arr. column ( idx) , field. data_type ( ) )
175+ } )
176+ . collect ( ) ;
177+ Ok ( TypedArray :: Struct ( struct_arr, fields. clone ( ) , typed_fields) )
178+ }
171179 DataType :: List ( field) => Ok ( TypedArray :: List (
172180 array. as_any ( ) . downcast_ref :: < ListArray > ( ) . ok_or_else ( || {
173181 CometError :: Internal ( "Failed to downcast to ListArray" . to_string ( ) )
@@ -217,7 +225,7 @@ impl<'a> TypedArray<'a> {
217225 TypedArray :: LargeString ( arr) => arr. is_null ( row_idx) ,
218226 TypedArray :: Binary ( arr) => arr. is_null ( row_idx) ,
219227 TypedArray :: LargeBinary ( arr) => arr. is_null ( row_idx) ,
220- TypedArray :: Struct ( arr, _) => arr. is_null ( row_idx) ,
228+ TypedArray :: Struct ( arr, _, _ ) => arr. is_null ( row_idx) ,
221229 TypedArray :: List ( arr, _) => arr. is_null ( row_idx) ,
222230 TypedArray :: LargeList ( arr, _) => arr. is_null ( row_idx) ,
223231 TypedArray :: Map ( arr, _) => arr. is_null ( row_idx) ,
@@ -317,7 +325,9 @@ impl<'a> TypedArray<'a> {
317325 buffer. extend ( std:: iter:: repeat_n ( 0u8 , padding) ) ;
318326 Ok ( len)
319327 }
320- TypedArray :: Struct ( arr, fields) => write_struct_to_buffer ( buffer, arr, row_idx, fields) ,
328+ TypedArray :: Struct ( arr, fields, typed_fields) => {
329+ write_struct_to_buffer_typed ( buffer, arr, row_idx, fields, typed_fields)
330+ }
321331 TypedArray :: List ( arr, field) => write_list_to_buffer ( buffer, arr, row_idx, field) ,
322332 TypedArray :: LargeList ( arr, field) => {
323333 write_large_list_to_buffer ( buffer, arr, row_idx, field)
@@ -1840,10 +1850,63 @@ fn write_array_element(buffer: &mut [u8], data_type: &DataType, value: i64, offs
18401850// These write directly to the output buffer to avoid intermediate allocations.
18411851// =============================================================================
18421852
1853+ /// Writes a struct value directly to the buffer using pre-downcast typed fields.
1854+ /// Returns the unpadded length written.
1855+ ///
1856+ /// This version uses pre-downcast TypedElements for each field, eliminating
1857+ /// per-row type dispatch overhead.
1858+ #[ inline]
1859+ fn write_struct_to_buffer_typed (
1860+ buffer : & mut Vec < u8 > ,
1861+ _struct_array : & StructArray ,
1862+ row_idx : usize ,
1863+ _fields : & arrow:: datatypes:: Fields ,
1864+ typed_fields : & [ TypedElements ] ,
1865+ ) -> CometResult < usize > {
1866+ let num_fields = typed_fields. len ( ) ;
1867+ let nested_bitset_width = ColumnarToRowContext :: calculate_bitset_width ( num_fields) ;
1868+ let nested_fixed_size = nested_bitset_width + num_fields * 8 ;
1869+
1870+ // Remember where this struct starts in the buffer
1871+ let struct_start = buffer. len ( ) ;
1872+
1873+ // Reserve space for fixed-width portion (zeros for null bits and field slots)
1874+ buffer. resize ( struct_start + nested_fixed_size, 0 ) ;
1875+
1876+ // Write each field using pre-downcast types
1877+ for ( field_idx, typed_field) in typed_fields. iter ( ) . enumerate ( ) {
1878+ if typed_field. is_null_at ( row_idx) {
1879+ // Set null bit in nested struct
1880+ set_null_bit ( buffer, struct_start, field_idx) ;
1881+ } else {
1882+ let field_offset = struct_start + nested_bitset_width + field_idx * 8 ;
1883+
1884+ if typed_field. is_fixed_width ( ) {
1885+ // Fixed-width field - use pre-downcast accessor
1886+ let value = typed_field. get_fixed_value ( row_idx) ;
1887+ buffer[ field_offset..field_offset + 8 ] . copy_from_slice ( & value. to_le_bytes ( ) ) ;
1888+ } else {
1889+ // Variable-length field - use pre-downcast writer
1890+ let var_len = typed_field. write_variable_value ( buffer, row_idx, struct_start) ?;
1891+ if var_len > 0 {
1892+ let padded_len = round_up_to_8 ( var_len) ;
1893+ let data_offset = buffer. len ( ) - padded_len - struct_start;
1894+ let offset_and_len = ( ( data_offset as i64 ) << 32 ) | ( var_len as i64 ) ;
1895+ buffer[ field_offset..field_offset + 8 ]
1896+ . copy_from_slice ( & offset_and_len. to_le_bytes ( ) ) ;
1897+ }
1898+ }
1899+ }
1900+ }
1901+
1902+ Ok ( buffer. len ( ) - struct_start)
1903+ }
1904+
18431905/// Writes a struct value directly to the buffer.
18441906/// Returns the unpadded length written.
18451907///
18461908/// Processes each field using inline type dispatch to avoid allocation overhead.
1909+ /// This is used for nested structs where we don't have pre-downcast fields.
18471910#[ inline]
18481911fn write_struct_to_buffer (
18491912 buffer : & mut Vec < u8 > ,
0 commit comments