@@ -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,23 +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+ let len = if s == 0 {
172+ // Note that for degenerate and non-nullable `FixedSizeList`s, we will set the length to
173+ // 0 (`_or_default`).
174+ nulls. as_ref ( ) . map ( |x| x. len ( ) ) . unwrap_or_default ( )
175+ } else {
176+ if values. len ( ) % s != 0 {
177+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
178+ "Incorrect length of values buffer for FixedSizeListArray, \
179+ expected a multiple of {s} got {}",
180+ values. len( ) ,
181+ ) ) ) ;
182+ }
183+
184+ let len = values. len ( ) / s;
185+
186+ // Check that the null buffer length is correct (if it exists).
187+ if let Some ( null_buffer) = & nulls {
188+ if s * null_buffer. len ( ) != values. len ( ) {
189+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
190+ "Incorrect length of values buffer for FixedSizeListArray, expected {} got {}" ,
191+ s * null_buffer. len( ) ,
192+ values. len( ) ,
193+ ) ) ) ;
167194 }
168- len
169195 }
196+
197+ len
170198 } ;
171199
200+ Self :: try_new_with_length ( field, size, values, nulls, len)
201+ }
202+
203+ /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure.
204+ ///
205+ /// This method exists to allow the construction of arbitrary length degenerate (`size == 0`)
206+ /// and non-nullable `FixedSizeListArray`s. If you want a nullable `FixedSizeListArray`, then
207+ /// you can use [`try_new()`] instead.
208+ ///
209+ /// [`try_new()`]: Self::try_new
210+ ///
211+ /// # Errors
212+ ///
213+ /// * `size < 0`
214+ /// * `nulls.len() != len` if `nulls` is `Some`
215+ /// * `values.len() != len * size`
216+ /// * `values.data_type() != field.data_type()`
217+ /// * `!field.is_nullable() && !nulls.expand(size).contains(values.logical_nulls())`
218+ pub fn try_new_with_length (
219+ field : FieldRef ,
220+ size : i32 ,
221+ values : ArrayRef ,
222+ nulls : Option < NullBuffer > ,
223+ len : usize ,
224+ ) -> Result < Self , ArrowError > {
225+ let s = size. to_usize ( ) . ok_or_else ( || {
226+ ArrowError :: InvalidArgumentError ( format ! ( "Size cannot be negative, got {size}" ) )
227+ } ) ?;
228+
229+ if let Some ( null_buffer) = & nulls
230+ && null_buffer. len ( ) != len
231+ {
232+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
233+ "Invalid null buffer for FixedSizeListArray, expected {len} found {}" ,
234+ null_buffer. len( )
235+ ) ) ) ;
236+ }
237+
238+ if s == 0 && !values. is_empty ( ) {
239+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
240+ "An degenerate FixedSizeListArray should have no underlying values, found {} values" ,
241+ values. len( )
242+ ) ) ) ;
243+ }
244+
245+ if values. len ( ) != len * s {
246+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
247+ "Incorrect length of values buffer for FixedSizeListArray, expected {} got {}" ,
248+ len * s,
249+ values. len( ) ,
250+ ) ) ) ;
251+ }
252+
172253 if field. data_type ( ) != values. data_type ( ) {
173254 return Err ( ArrowError :: InvalidArgumentError ( format ! (
174255 "FixedSizeListArray expected data type {} got {} for {:?}" ,
@@ -673,23 +754,35 @@ mod tests {
673754 let list = FixedSizeListArray :: new ( field. clone ( ) , 2 , values. clone ( ) , Some ( nulls) ) ;
674755 assert_eq ! ( list. len( ) , 3 ) ;
675756
676- let list = FixedSizeListArray :: new ( field. clone ( ) , 4 , values. clone ( ) , None ) ;
677- assert_eq ! ( list. len( ) , 1 ) ;
757+ let list = FixedSizeListArray :: new ( field. clone ( ) , 3 , values. clone ( ) , None ) ;
758+ assert_eq ! ( list. len( ) , 2 ) ;
759+
760+ let err = FixedSizeListArray :: try_new ( field. clone ( ) , 4 , values. clone ( ) , None ) . unwrap_err ( ) ;
761+ assert_eq ! (
762+ err. to_string( ) ,
763+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, \
764+ expected a multiple of 4 got 6",
765+ ) ;
766+
767+ let err =
768+ FixedSizeListArray :: try_new_with_length ( field. clone ( ) , 4 , values. clone ( ) , None , 1 )
769+ . unwrap_err ( ) ;
770+ assert_eq ! (
771+ err. to_string( ) ,
772+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
773+ ) ;
678774
679775 let err = FixedSizeListArray :: try_new ( field. clone ( ) , -1 , values. clone ( ) , None ) . unwrap_err ( ) ;
680776 assert_eq ! (
681777 err. to_string( ) ,
682778 "Invalid argument error: Size cannot be negative, got -1"
683779 ) ;
684780
685- let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values. clone ( ) , None ) ;
686- assert_eq ! ( list. len( ) , 0 ) ;
687-
688781 let nulls = NullBuffer :: new_null ( 2 ) ;
689782 let err = FixedSizeListArray :: try_new ( field, 2 , values. clone ( ) , Some ( nulls) ) . unwrap_err ( ) ;
690783 assert_eq ! (
691784 err. to_string( ) ,
692- "Invalid argument error: Incorrect length of null buffer for FixedSizeListArray, expected 3 got 2 "
785+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6 "
693786 ) ;
694787
695788 let field = Arc :: new ( Field :: new_list_field ( DataType :: Int32 , false ) ) ;
@@ -712,11 +805,35 @@ mod tests {
712805 }
713806
714807 #[ test]
715- fn empty_fixed_size_list ( ) {
808+ fn degenerate_fixed_size_list ( ) {
716809 let field = Arc :: new ( Field :: new_list_field ( DataType :: Int32 , true ) ) ;
717810 let nulls = NullBuffer :: new_null ( 2 ) ;
718811 let values = new_empty_array ( & DataType :: Int32 ) ;
719- let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values, Some ( nulls) ) ;
812+ let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values. clone ( ) , Some ( nulls. clone ( ) ) ) ;
720813 assert_eq ! ( list. len( ) , 2 ) ;
814+
815+ // Test invalid null buffer length.
816+ let err = FixedSizeListArray :: try_new_with_length (
817+ field. clone ( ) ,
818+ 0 ,
819+ values. clone ( ) ,
820+ Some ( nulls) ,
821+ 5 ,
822+ )
823+ . unwrap_err ( ) ;
824+ assert_eq ! (
825+ err. to_string( ) ,
826+ "Invalid argument error: Invalid null buffer for FixedSizeListArray, expected 5 found 2"
827+ ) ;
828+
829+ // Test non-empty values for degenerate list.
830+ let non_empty_values = Arc :: new ( Int32Array :: from ( vec ! [ 1 , 2 , 3 ] ) ) ;
831+ let err =
832+ FixedSizeListArray :: try_new_with_length ( field. clone ( ) , 0 , non_empty_values, None , 3 )
833+ . unwrap_err ( ) ;
834+ assert_eq ! (
835+ err. to_string( ) ,
836+ "Invalid argument error: An degenerate FixedSizeListArray should have no underlying values, found 3 values"
837+ ) ;
721838 }
722839}
0 commit comments