@@ -125,7 +125,15 @@ pub struct FixedSizeListArray {
125125}
126126
127127impl FixedSizeListArray {
128- /// Create a new [`FixedSizeListArray`] with `size` element size, panicking on failure
128+ /// Create a new [`FixedSizeListArray`] with `size` element size, panicking on failure.
129+ ///
130+ /// Note that if `size == 0` and `nulls` is `None` (a degenerate, non-nullable
131+ /// `FixedSizeListArray`), this function will set the length of the array to 0.
132+ ///
133+ /// If you would like to have a degenerate, non-nullable `FixedSizeListArray` with arbitrary
134+ /// length, use the [`try_new_with_length()`] constructor.
135+ ///
136+ /// [`try_new_with_length()`]: Self::try_new_with_length
129137 ///
130138 /// # Panics
131139 ///
@@ -134,12 +142,20 @@ impl FixedSizeListArray {
134142 Self :: try_new ( field, size, values, nulls) . unwrap ( )
135143 }
136144
137- /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure
145+ /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure.
146+ ///
147+ /// Note that if `size == 0` and `nulls` is `None` (a degenerate, non-nullable
148+ /// `FixedSizeListArray`), this function will set the length of the array to 0.
149+ ///
150+ /// If you would like to have a degenerate, non-nullable `FixedSizeListArray` with arbitrary
151+ /// length, use the [`try_new_with_length()`] constructor.
152+ ///
153+ /// [`try_new_with_length()`]: Self::try_new_with_length
138154 ///
139155 /// # Errors
140156 ///
141157 /// * `size < 0`
142- /// * `values.len() / size != nulls.len()`
158+ /// * `values.len() != nulls.len() * size` if `nulls` is `Some `
143159 /// * `values.data_type() != field.data_type()`
144160 /// * `!field.is_nullable() && !nulls.expand(size).contains(values.logical_nulls())`
145161 pub fn try_new (
@@ -152,22 +168,88 @@ impl FixedSizeListArray {
152168 ArrowError :: InvalidArgumentError ( format ! ( "Size cannot be negative, got {size}" ) )
153169 } ) ?;
154170
155- let len = match s {
156- 0 => nulls. as_ref ( ) . map ( |x| x. len ( ) ) . unwrap_or_default ( ) ,
157- _ => {
158- let len = values. len ( ) / s. max ( 1 ) ;
159- if let Some ( n) = nulls. as_ref ( ) {
160- if n. len ( ) != len {
161- return Err ( ArrowError :: InvalidArgumentError ( format ! (
162- "Incorrect length of null buffer for FixedSizeListArray, expected {} got {}" ,
163- len,
164- n. len( ) ,
165- ) ) ) ;
166- }
171+ if s == 0 {
172+ // Note that for degenerate (`size == 0`) and non-nullable `FixedSizeList`s, we will set
173+ // the length to 0 (`_or_default`).
174+ let len = nulls. as_ref ( ) . map ( |x| x. len ( ) ) . unwrap_or_default ( ) ;
175+
176+ Self :: try_new_with_length ( field, size, values, nulls, len)
177+ } else {
178+ if values. len ( ) % s != 0 {
179+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
180+ "Incorrect length of values buffer for FixedSizeListArray, \
181+ expected a multiple of {s} got {}",
182+ values. len( ) ,
183+ ) ) ) ;
184+ }
185+
186+ let len = values. len ( ) / s;
187+
188+ // Check that the null buffer length is correct (if it exists).
189+ if let Some ( null_buffer) = & nulls {
190+ if s * null_buffer. len ( ) != values. len ( ) {
191+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
192+ "Incorrect length of values buffer for FixedSizeListArray, \
193+ expected {} got {}",
194+ s * null_buffer. len( ) ,
195+ values. len( ) ,
196+ ) ) ) ;
167197 }
168- len
169198 }
170- } ;
199+
200+ Self :: try_new_with_length ( field, size, values, nulls, len)
201+ }
202+ }
203+
204+ /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure.
205+ ///
206+ /// This method exists to allow the construction of arbitrary length degenerate (`size == 0`)
207+ /// and non-nullable `FixedSizeListArray`s. If you want a nullable `FixedSizeListArray`, then
208+ /// you can use [`try_new()`] instead.
209+ ///
210+ /// [`try_new()`]: Self::try_new
211+ ///
212+ /// # Errors
213+ ///
214+ /// * `size < 0`
215+ /// * `nulls.len() != len` if `nulls` is `Some`
216+ /// * `values.len() != len * size`
217+ /// * `values.data_type() != field.data_type()`
218+ /// * `!field.is_nullable() && !nulls.expand(size).contains(values.logical_nulls())`
219+ pub fn try_new_with_length (
220+ field : FieldRef ,
221+ size : i32 ,
222+ values : ArrayRef ,
223+ nulls : Option < NullBuffer > ,
224+ len : usize ,
225+ ) -> Result < Self , ArrowError > {
226+ let s = size. to_usize ( ) . ok_or_else ( || {
227+ ArrowError :: InvalidArgumentError ( format ! ( "Size cannot be negative, got {size}" ) )
228+ } ) ?;
229+
230+ if let Some ( null_buffer) = & nulls {
231+ if null_buffer. len ( ) != len {
232+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
233+ "Invalid null buffer for FixedSizeListArray, expected {len} found {}" ,
234+ null_buffer. len( )
235+ ) ) ) ;
236+ }
237+ }
238+
239+ if s == 0 && !values. is_empty ( ) {
240+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
241+ "An degenerate FixedSizeListArray should have no underlying values, found {} values" ,
242+ values. len( )
243+ ) ) ) ;
244+ }
245+
246+ if values. len ( ) != len * s {
247+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
248+ "Incorrect length of values buffer for FixedSizeListArray, expected {} got {}" ,
249+ len * s,
250+ values. len( ) ,
251+ ) ) ) ;
252+ }
171253
172254 if field. data_type ( ) != values. data_type ( ) {
173255 return Err ( ArrowError :: InvalidArgumentError ( format ! (
@@ -673,23 +755,35 @@ mod tests {
673755 let list = FixedSizeListArray :: new ( field. clone ( ) , 2 , values. clone ( ) , Some ( nulls) ) ;
674756 assert_eq ! ( list. len( ) , 3 ) ;
675757
676- let list = FixedSizeListArray :: new ( field. clone ( ) , 4 , values. clone ( ) , None ) ;
677- assert_eq ! ( list. len( ) , 1 ) ;
758+ let list = FixedSizeListArray :: new ( field. clone ( ) , 3 , values. clone ( ) , None ) ;
759+ assert_eq ! ( list. len( ) , 2 ) ;
760+
761+ let err = FixedSizeListArray :: try_new ( field. clone ( ) , 4 , values. clone ( ) , None ) . unwrap_err ( ) ;
762+ assert_eq ! (
763+ err. to_string( ) ,
764+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, \
765+ expected a multiple of 4 got 6",
766+ ) ;
767+
768+ let err =
769+ FixedSizeListArray :: try_new_with_length ( field. clone ( ) , 4 , values. clone ( ) , None , 1 )
770+ . unwrap_err ( ) ;
771+ assert_eq ! (
772+ err. to_string( ) ,
773+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
774+ ) ;
678775
679776 let err = FixedSizeListArray :: try_new ( field. clone ( ) , -1 , values. clone ( ) , None ) . unwrap_err ( ) ;
680777 assert_eq ! (
681778 err. to_string( ) ,
682779 "Invalid argument error: Size cannot be negative, got -1"
683780 ) ;
684781
685- let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values. clone ( ) , None ) ;
686- assert_eq ! ( list. len( ) , 0 ) ;
687-
688782 let nulls = NullBuffer :: new_null ( 2 ) ;
689783 let err = FixedSizeListArray :: try_new ( field, 2 , values. clone ( ) , Some ( nulls) ) . unwrap_err ( ) ;
690784 assert_eq ! (
691785 err. to_string( ) ,
692- "Invalid argument error: Incorrect length of null buffer for FixedSizeListArray, expected 3 got 2 "
786+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6 "
693787 ) ;
694788
695789 let field = Arc :: new ( Field :: new_list_field ( DataType :: Int32 , false ) ) ;
@@ -712,11 +806,35 @@ mod tests {
712806 }
713807
714808 #[ test]
715- fn empty_fixed_size_list ( ) {
809+ fn degenerate_fixed_size_list ( ) {
716810 let field = Arc :: new ( Field :: new_list_field ( DataType :: Int32 , true ) ) ;
717811 let nulls = NullBuffer :: new_null ( 2 ) ;
718812 let values = new_empty_array ( & DataType :: Int32 ) ;
719- let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values, Some ( nulls) ) ;
813+ let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values. clone ( ) , Some ( nulls. clone ( ) ) ) ;
720814 assert_eq ! ( list. len( ) , 2 ) ;
815+
816+ // Test invalid null buffer length.
817+ let err = FixedSizeListArray :: try_new_with_length (
818+ field. clone ( ) ,
819+ 0 ,
820+ values. clone ( ) ,
821+ Some ( nulls) ,
822+ 5 ,
823+ )
824+ . unwrap_err ( ) ;
825+ assert_eq ! (
826+ err. to_string( ) ,
827+ "Invalid argument error: Invalid null buffer for FixedSizeListArray, expected 5 found 2"
828+ ) ;
829+
830+ // Test non-empty values for degenerate list.
831+ let non_empty_values = Arc :: new ( Int32Array :: from ( vec ! [ 1 , 2 , 3 ] ) ) ;
832+ let err =
833+ FixedSizeListArray :: try_new_with_length ( field. clone ( ) , 0 , non_empty_values, None , 3 )
834+ . unwrap_err ( ) ;
835+ assert_eq ! (
836+ err. to_string( ) ,
837+ "Invalid argument error: An degenerate FixedSizeListArray should have no underlying values, found 3 values"
838+ ) ;
721839 }
722840}
0 commit comments