@@ -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,79 @@ 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+ // Check that the null buffer length is correct (if it exists).
177+ let len = values. len ( ) / s;
178+
179+ if let Some ( null_buffer) = & nulls {
180+ if null_buffer. len ( ) != len {
181+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
182+ "Incorrect length of null buffer for FixedSizeListArray, expected {len} got {}" ,
183+ null_buffer. len( ) ,
184+ ) ) ) ;
167185 }
168- len
169186 }
187+
188+ len
170189 } ;
171190
191+ Self :: try_new_with_length ( field, size, values, nulls, len)
192+ }
193+
194+ /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure.
195+ ///
196+ /// This method exists to allow the construction of arbitrary length degenerate (`size == 0`)
197+ /// and non-nullable `FixedSizeListArray`s. If you want a nullable `FixedSizeListArray`, then
198+ /// you can use [`try_new()`] instead.
199+ ///
200+ /// [`try_new()`]: Self::try_new
201+ ///
202+ /// # Errors
203+ ///
204+ /// * `size < 0`
205+ /// * `nulls.len() != len` if `nulls` is `Some`
206+ /// * `values.len() != len * size`
207+ /// * `values.data_type() != field.data_type()`
208+ /// * `!field.is_nullable() && !nulls.expand(size).contains(values.logical_nulls())`
209+ pub fn try_new_with_length (
210+ field : FieldRef ,
211+ size : i32 ,
212+ values : ArrayRef ,
213+ nulls : Option < NullBuffer > ,
214+ len : usize ,
215+ ) -> Result < Self , ArrowError > {
216+ let s = size. to_usize ( ) . ok_or_else ( || {
217+ ArrowError :: InvalidArgumentError ( format ! ( "Size cannot be negative, got {size}" ) )
218+ } ) ?;
219+
220+ if let Some ( null_buffer) = & nulls
221+ && null_buffer. len ( ) != len
222+ {
223+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
224+ "Invalid null buffer for FixedSizeListArray, expected {len} found {}" ,
225+ null_buffer. len( )
226+ ) ) ) ;
227+ }
228+
229+ if s == 0 && !values. is_empty ( ) {
230+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
231+ "An degenerate FixedSizeListArray should have no underlying values, found {} values" ,
232+ values. len( )
233+ ) ) ) ;
234+ }
235+
236+ if values. len ( ) != len * s {
237+ return Err ( ArrowError :: InvalidArgumentError ( format ! (
238+ "Incorrect length of values buffer for FixedSizeListArray, expected {} got {}" ,
239+ len * s,
240+ values. len( ) ,
241+ ) ) ) ;
242+ }
243+
172244 if field. data_type ( ) != values. data_type ( ) {
173245 return Err ( ArrowError :: InvalidArgumentError ( format ! (
174246 "FixedSizeListArray expected data type {} got {} for {:?}" ,
@@ -673,18 +745,23 @@ mod tests {
673745 let list = FixedSizeListArray :: new ( field. clone ( ) , 2 , values. clone ( ) , Some ( nulls) ) ;
674746 assert_eq ! ( list. len( ) , 3 ) ;
675747
676- let list = FixedSizeListArray :: new ( field. clone ( ) , 4 , values. clone ( ) , None ) ;
677- assert_eq ! ( list. len( ) , 1 ) ;
748+ let list = FixedSizeListArray :: new ( field. clone ( ) , 3 , values. clone ( ) , None ) ;
749+ assert_eq ! ( list. len( ) , 2 ) ;
750+
751+ let err =
752+ FixedSizeListArray :: try_new_with_length ( field. clone ( ) , 1 , values. clone ( ) , None , 4 )
753+ . unwrap_err ( ) ;
754+ assert_eq ! (
755+ err. to_string( ) ,
756+ "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
757+ ) ;
678758
679759 let err = FixedSizeListArray :: try_new ( field. clone ( ) , -1 , values. clone ( ) , None ) . unwrap_err ( ) ;
680760 assert_eq ! (
681761 err. to_string( ) ,
682762 "Invalid argument error: Size cannot be negative, got -1"
683763 ) ;
684764
685- let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values. clone ( ) , None ) ;
686- assert_eq ! ( list. len( ) , 0 ) ;
687-
688765 let nulls = NullBuffer :: new_null ( 2 ) ;
689766 let err = FixedSizeListArray :: try_new ( field, 2 , values. clone ( ) , Some ( nulls) ) . unwrap_err ( ) ;
690767 assert_eq ! (
@@ -704,19 +781,43 @@ mod tests {
704781 FixedSizeListArray :: new ( field, 2 , values. clone ( ) , Some ( nulls) ) ;
705782
706783 let field = Arc :: new ( Field :: new_list_field ( DataType :: Int64 , true ) ) ;
707- let err = FixedSizeListArray :: try_new ( field, 2 , values, None ) . unwrap_err ( ) ;
784+ let err = FixedSizeListArray :: try_new ( field. clone ( ) , 2 , values, None ) . unwrap_err ( ) ;
708785 assert_eq ! (
709786 err. to_string( ) ,
710787 "Invalid argument error: FixedSizeListArray expected data type Int64 got Int32 for \" item\" "
711788 ) ;
712789 }
713790
714791 #[ test]
715- fn empty_fixed_size_list ( ) {
792+ fn degenerate_fixed_size_list ( ) {
716793 let field = Arc :: new ( Field :: new_list_field ( DataType :: Int32 , true ) ) ;
717794 let nulls = NullBuffer :: new_null ( 2 ) ;
718795 let values = new_empty_array ( & DataType :: Int32 ) ;
719- let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values, Some ( nulls) ) ;
796+ let list = FixedSizeListArray :: new ( field. clone ( ) , 0 , values. clone ( ) , Some ( nulls. clone ( ) ) ) ;
720797 assert_eq ! ( list. len( ) , 2 ) ;
798+
799+ // Test invalid null buffer length.
800+ let err = FixedSizeListArray :: try_new_with_length (
801+ field. clone ( ) ,
802+ 0 ,
803+ values. clone ( ) ,
804+ Some ( nulls) ,
805+ 5 ,
806+ )
807+ . unwrap_err ( ) ;
808+ assert_eq ! (
809+ err. to_string( ) ,
810+ "Invalid argument error: Invalid null buffer for FixedSizeListArray, expected 5 found 2"
811+ ) ;
812+
813+ // Test non-empty values for degenerate list.
814+ let non_empty_values = Arc :: new ( Int32Array :: from ( vec ! [ 1 , 2 , 3 ] ) ) ;
815+ let err =
816+ FixedSizeListArray :: try_new_with_length ( field. clone ( ) , 0 , non_empty_values, None , 3 )
817+ . unwrap_err ( ) ;
818+ assert_eq ! (
819+ err. to_string( ) ,
820+ "Invalid argument error: An degenerate FixedSizeListArray should have no underlying values, found 3 values"
821+ ) ;
721822 }
722823}
0 commit comments