@@ -14,6 +14,8 @@ use futures::future::BoxFuture;
1414use futures:: stream:: BoxStream ;
1515use itertools:: Itertools ;
1616use vortex_array:: ArrayRef ;
17+ use vortex_array:: IntoArray ;
18+ use vortex_array:: arrays:: PrimitiveArray ;
1719use vortex_array:: dtype:: DType ;
1820use vortex_array:: dtype:: Field ;
1921use vortex_array:: dtype:: FieldMask ;
@@ -55,6 +57,10 @@ pub struct ScanBuilder<A> {
5557 filter : Option < Expression > ,
5658 /// Whether the scan needs to return splits in the order they appear in the file.
5759 ordered : bool ,
60+ /// Whether to yield chunks in reverse file order, with rows within each chunk also reversed.
61+ ///
62+ /// Implies ordered output (chunks are emitted in strict reverse sequence, not interleaved).
63+ reversed : bool ,
5864 /// Optionally read a subset of the rows in the file.
5965 row_range : Option < Range < u64 > > ,
6066 /// The selection mask to apply to the selected row range.
@@ -95,6 +101,7 @@ impl ScanBuilder<ArrayRef> {
95101 file_stats : None ,
96102 limit : None ,
97103 row_offset : 0 ,
104+ reversed : false ,
98105 }
99106 }
100107
@@ -146,6 +153,20 @@ impl<A: 'static + Send> ScanBuilder<A> {
146153 self
147154 }
148155
156+ pub fn reversed ( & self ) -> bool {
157+ self . reversed
158+ }
159+
160+ /// Reverse the scan order: chunks are yielded last-to-first, and rows within each chunk are
161+ /// also reversed. This produces a globally reversed row sequence without reading the whole
162+ /// file first.
163+ ///
164+ /// Reversed scans always produce ordered output (equivalent to `with_ordered(true)`).
165+ pub fn with_reversed ( mut self , reversed : bool ) -> Self {
166+ self . reversed = reversed;
167+ self
168+ }
169+
149170 pub fn with_row_range ( mut self , row_range : Range < u64 > ) -> Self {
150171 self . row_range = Some ( row_range) ;
151172 self
@@ -233,6 +254,7 @@ impl<A: 'static + Send> ScanBuilder<A> {
233254 file_stats : self . file_stats ,
234255 limit : self . limit ,
235256 row_offset : self . row_offset ,
257+ reversed : self . reversed ,
236258 map_fn : Arc :: new ( move |a| old_map_fn ( a) . and_then ( & map_fn) ) ,
237259 }
238260 }
@@ -285,6 +307,19 @@ impl<A: 'static + Send> ScanBuilder<A> {
285307 ) ?)
286308 } ;
287309
310+ // If reversed, wrap the map_fn to reverse row order within each chunk via a lazy
311+ // `DictArray` take. Chunk order reversal is handled by `RepeatedScan::execute`.
312+ let map_fn = if self . reversed {
313+ let original = self . map_fn ;
314+ Arc :: new ( move |array : ArrayRef | {
315+ let n = array. len ( ) as u64 ;
316+ let indices = PrimitiveArray :: from_iter ( ( 0 ..n) . rev ( ) ) . into_array ( ) ;
317+ original ( array. take ( indices) ?)
318+ } ) as Arc < dyn Fn ( ArrayRef ) -> VortexResult < A > + Send + Sync >
319+ } else {
320+ self . map_fn
321+ } ;
322+
288323 Ok ( RepeatedScan :: new (
289324 self . session . clone ( ) ,
290325 layout_reader,
@@ -295,9 +330,10 @@ impl<A: 'static + Send> ScanBuilder<A> {
295330 self . selection ,
296331 splits,
297332 self . concurrency ,
298- self . map_fn ,
333+ map_fn,
299334 self . limit ,
300335 dtype,
336+ self . reversed ,
301337 ) )
302338 }
303339
@@ -366,7 +402,7 @@ impl<A: 'static + Send> Stream for LazyScanStream<A> {
366402 match & mut self . state {
367403 LazyScanState :: Builder ( builder) => {
368404 let builder = builder. take ( ) . vortex_expect ( "polled after completion" ) ;
369- let ordered = builder. ordered ;
405+ let ordered = builder. ordered || builder . reversed ;
370406 let num_workers = std:: thread:: available_parallelism ( )
371407 . map ( |n| n. get ( ) )
372408 . unwrap_or ( 1 ) ;
0 commit comments