@@ -20,7 +20,9 @@ use arrow::array::{Array, ArrayRef, OffsetSizeTrait};
2020use arrow:: datatypes:: ArrowNativeType as _;
2121use arrow:: datatypes:: DataType ;
2222
23- use crate :: datatype:: { ColumnError , InfallibleBuild , LogicalType , downcast_array} ;
23+ use crate :: datatype:: {
24+ ColumnError , InfallibleBuild , LogicalType , PrimitiveType , RefType , downcast_array,
25+ } ;
2426
2527/// Marker for an arrow `List` column with items of logical type `L`.
2628///
@@ -155,20 +157,189 @@ pub(crate) use impl_list_datatype;
155157
156158impl_list_datatype ! ( List , TypedList , arrow:: array:: ListArray , List ) ;
157159
158- /// One list element of a `Column<List<L>>`: an iterator over the typed items.
160+ /// One list element of a list column (`List`, [`LargeList`](crate::LargeList),
161+ /// [`FixedSizeList`](crate::FixedSizeList), …): a zero-copy, random-access view
162+ /// of that row's typed items.
163+ ///
164+ /// It mirrors [`Column`](crate::Column)'s read API — the items behave like a
165+ /// borrowed slice of a single-typed column:
166+ ///
167+ /// - length: [`len`](ListValue::len), [`is_empty`](ListValue::is_empty)
168+ /// - by-item access: [`get`](ListValue::get) / [`value`](ListValue::value)
169+ /// (zero-copy views) and [`get_owned`](ListValue::get_owned) /
170+ /// [`value_owned`](ListValue::value_owned) (owned), plus `list[i]` where the
171+ /// item can be borrowed from the array (see the [`Index`](std::ops::Index) impl)
172+ /// - bulk: [`to_vec`](ListValue::to_vec), and [`as_slice`](ListValue::as_slice)
173+ /// for primitive items (one contiguous zero-copy slice)
174+ /// - iteration: [`iter`](ListValue::iter) / [`iter_owned`](ListValue::iter_owned)
175+ ///
176+ /// `ListValue` is itself an [`Iterator`] over the items, so `.map(…)` /
177+ /// `.collect()` / `.sum()` work directly on it. It is [`Copy`]: consuming it as
178+ /// an iterator advances a cursor (the random-access methods then see the
179+ /// remaining items), while [`iter`](ListValue::iter) hands out a fresh cursor
180+ /// without consuming the original.
181+ ///
182+ /// ```
183+ /// use quiver::{Column, List};
184+ ///
185+ /// let column = Column::<List<i64>>::from_values([vec![10, 20, 30], vec![]]);
186+ /// let row = column.value(0);
187+ ///
188+ /// assert_eq!(row.len(), 3);
189+ /// assert_eq!(row.value(1), 20); // by item index
190+ /// assert_eq!(row[2], 30); // borrowed (primitive items)
191+ /// assert_eq!(row.as_slice(), &[10, 20, 30]); // contiguous, zero-copy
192+ /// assert_eq!(row.to_vec(), vec![10, 20, 30]);
193+ ///
194+ /// let sum: i64 = row.iter().sum(); // `iter` does not consume `row`
195+ /// assert_eq!(sum, 60);
196+ ///
197+ /// assert!(column.value(1).is_empty());
198+ /// ```
159199pub struct ListValue < ' a , L : LogicalType > {
160200 values : & ' a L :: Typed ,
161201 index : usize ,
162202 end : usize ,
163203}
164204
165- impl < ' a , L : LogicalType > ListValue < ' a , L > {
205+ impl < L : LogicalType > Clone for ListValue < ' _ , L > {
206+ fn clone ( & self ) -> Self {
207+ * self
208+ }
209+ }
210+
211+ impl < L : LogicalType > Copy for ListValue < ' _ , L > { }
212+
213+ impl < ' a , L : LogicalType + ' a > ListValue < ' a , L > {
166214 /// `index..end` into `values`.
167215 pub ( crate ) fn new ( values : & ' a L :: Typed , index : usize , end : usize ) -> Self {
168216 Self { values, index, end }
169217 }
218+
219+ /// The number of items in this list element.
220+ #[ must_use]
221+ pub fn len ( & self ) -> usize {
222+ self . end - self . index
223+ }
224+
225+ /// Is this list element empty?
226+ #[ must_use]
227+ pub fn is_empty ( & self ) -> bool {
228+ self . index == self . end
229+ }
230+
231+ /// The item at `index`, or `None` if out of bounds.
232+ ///
233+ /// See [`ListValue::value`] for the returned view;
234+ /// [`ListValue::get_owned`] returns the owned value instead.
235+ #[ must_use]
236+ pub fn get ( & self , index : usize ) -> Option < L :: Value < ' a > > {
237+ let item = self . index . checked_add ( index) ?;
238+ ( item < self . end ) . then ( || L :: value ( self . values , item) )
239+ }
240+
241+ /// The owned item at `index`, or `None` if out of bounds —
242+ /// e.g. `String` where [`ListValue::get`] returns `&str`.
243+ #[ must_use]
244+ pub fn get_owned ( & self , index : usize ) -> Option < L :: Owned > {
245+ self . get ( index) . map ( L :: to_owned_value)
246+ }
247+
248+ /// The item at `index`, returning the zero-copy view
249+ /// ([`LogicalType::Value`]); for the owned value see [`ListValue::value_owned`].
250+ ///
251+ /// Where the item can be borrowed from the array, `list[index]` works too
252+ /// (see the [`Index`](std::ops::Index) impl).
253+ ///
254+ /// Panics if out of bounds.
255+ #[ must_use]
256+ pub fn value ( & self , index : usize ) -> L :: Value < ' a > {
257+ assert ! (
258+ index < self . len( ) ,
259+ "ListValue index {index} out of bounds for length {}" ,
260+ self . len( )
261+ ) ;
262+ L :: value ( self . values , self . index + index)
263+ }
264+
265+ /// The owned item at `index` — e.g. `String` where [`ListValue::value`]
266+ /// returns `&str`.
267+ ///
268+ /// Panics if out of bounds.
269+ #[ must_use]
270+ pub fn value_owned ( & self , index : usize ) -> L :: Owned {
271+ L :: to_owned_value ( self . value ( index) )
272+ }
273+
274+ /// Iterates over the zero-copy views ([`LogicalType::Value`]) of the
275+ /// remaining items, without consuming `self`.
276+ ///
277+ /// For owned values, see [`ListValue::iter_owned`].
278+ #[ must_use]
279+ pub fn iter ( & self ) -> Self {
280+ * self
281+ }
282+
283+ /// Iterates over the owned values of the remaining items —
284+ /// e.g. `String` where [`ListValue::iter`] yields `&str`.
285+ pub fn iter_owned ( & self ) -> impl Iterator < Item = L :: Owned > + ' a {
286+ self . iter ( ) . map ( L :: to_owned_value)
287+ }
288+
289+ /// Copies the items into a `Vec` of owned values,
290+ /// e.g. `Vec<String>` for a `List<Utf8>` element.
291+ #[ must_use]
292+ pub fn to_vec ( self ) -> Vec < L :: Owned > {
293+ self . iter_owned ( ) . collect ( )
294+ }
170295}
171296
297+ /// `for item in &list` — iterates the items without consuming the view.
298+ impl < ' a , L : LogicalType + ' a > IntoIterator for & ListValue < ' a , L > {
299+ type Item = L :: Value < ' a > ;
300+ type IntoIter = ListValue < ' a , L > ;
301+
302+ fn into_iter ( self ) -> Self :: IntoIter {
303+ * self
304+ }
305+ }
306+
307+ /// `list[index]`: like [`ListValue::value`], but borrows from the array —
308+ /// `&list[i]` is `&str` for a `List<Utf8>` element, `&i64` for `List<i64>`.
309+ ///
310+ /// Available for items that can be borrowed from the array: strings, binaries,
311+ /// and primitives — but not `bool`, `Option<…>`, or nested `List<…>` items.
312+ ///
313+ /// Panics if out of bounds (like [`ListValue::value`]).
314+ impl < L : RefType > std:: ops:: Index < usize > for ListValue < ' _ , L > {
315+ type Output = L :: Ref ;
316+
317+ fn index ( & self , index : usize ) -> & Self :: Output {
318+ assert ! (
319+ index < self . len( ) ,
320+ "ListValue index {index} out of bounds for length {}" ,
321+ self . len( )
322+ ) ;
323+ L :: value_ref ( self . values , self . index + index)
324+ }
325+ }
326+
327+ impl < ' a , L : PrimitiveType > ListValue < ' a , L > {
328+ /// The items as a contiguous zero-copy slice,
329+ /// e.g. `&[f32]` for a `List<f32>` element.
330+ ///
331+ /// Only available for primitive and fixed-size binary items
332+ /// (`bool` is excluded: arrow bit-packs it).
333+ #[ must_use]
334+ pub fn as_slice ( & self ) -> & ' a [ L :: Native ] {
335+ & L :: values ( self . values ) [ self . index ..self . end ]
336+ }
337+ }
338+
339+ // Iteration mirrors a slice's: the items live in `self.index..self.end`, all
340+ // in bounds, so the combinators are overridden to skip the per-element `Option`
341+ // plumbing of the default `next`-based implementations. (Primitive items have
342+ // an even faster path: [`ListValue::as_slice`].)
172343impl < ' a , L : LogicalType + ' a > Iterator for ListValue < ' a , L > {
173344 type Item = L :: Value < ' a > ;
174345
@@ -186,10 +357,81 @@ impl<'a, L: LogicalType + 'a> Iterator for ListValue<'a, L> {
186357 let remaining = self . end - self . index ;
187358 ( remaining, Some ( remaining) )
188359 }
360+
361+ fn count ( self ) -> usize {
362+ self . end - self . index
363+ }
364+
365+ fn last ( self ) -> Option < Self :: Item > {
366+ ( self . index < self . end ) . then ( || L :: value ( self . values , self . end - 1 ) )
367+ }
368+
369+ fn nth ( & mut self , n : usize ) -> Option < Self :: Item > {
370+ match self . index . checked_add ( n) {
371+ Some ( target) if target < self . end => {
372+ self . index = target + 1 ;
373+ Some ( L :: value ( self . values , target) )
374+ }
375+ _ => {
376+ self . index = self . end ;
377+ None
378+ }
379+ }
380+ }
381+
382+ fn fold < B , F > ( self , init : B , mut f : F ) -> B
383+ where
384+ F : FnMut ( B , Self :: Item ) -> B ,
385+ {
386+ let Self { values, index, end } = self ;
387+ let mut acc = init;
388+ for i in index..end {
389+ acc = f ( acc, L :: value ( values, i) ) ;
390+ }
391+ acc
392+ }
393+ }
394+
395+ impl < ' a , L : LogicalType + ' a > DoubleEndedIterator for ListValue < ' a , L > {
396+ fn next_back ( & mut self ) -> Option < Self :: Item > {
397+ if self . index < self . end {
398+ self . end -= 1 ;
399+ Some ( L :: value ( self . values , self . end ) )
400+ } else {
401+ None
402+ }
403+ }
404+
405+ fn nth_back ( & mut self , n : usize ) -> Option < Self :: Item > {
406+ match self . end . checked_sub ( n + 1 ) {
407+ Some ( target) if self . index <= target => {
408+ self . end = target;
409+ Some ( L :: value ( self . values , target) )
410+ }
411+ _ => {
412+ self . end = self . index ;
413+ None
414+ }
415+ }
416+ }
417+
418+ fn rfold < B , F > ( self , init : B , mut f : F ) -> B
419+ where
420+ F : FnMut ( B , Self :: Item ) -> B ,
421+ {
422+ let Self { values, index, end } = self ;
423+ let mut acc = init;
424+ for i in ( index..end) . rev ( ) {
425+ acc = f ( acc, L :: value ( values, i) ) ;
426+ }
427+ acc
428+ }
189429}
190430
191431impl < ' a , L : LogicalType + ' a > ExactSizeIterator for ListValue < ' a , L > { }
192432
433+ impl < ' a , L : LogicalType + ' a > std:: iter:: FusedIterator for ListValue < ' a , L > { }
434+
193435/// Counts the nulls among the *reachable* items of a list array (`List` or
194436/// `LargeList` — it is generic over the offset width):
195437/// items inside the ranges of valid (non-null) rows.
0 commit comments