@@ -146,25 +146,35 @@ fn finish_sort<'on_flush>(
146146struct RunCursor < ' source > {
147147 remaining_segments : usize ,
148148 reader : SegmentReader < ' source , BufReader < File > , SortRow > ,
149+ head : Option < SortRow > ,
149150}
150151
151152impl < ' source > RunCursor < ' source > {
152153 fn new ( source : & ' source SpillReader < SortRow > , run : & Run ) -> Result < Self , DatabaseError > {
153154 let mut reader = source. open_segment_reader ( ) ?;
154155 reader. reset ( run. first_segment ) ?;
155- Ok ( Self {
156+ let mut cursor = Self {
156157 remaining_segments : run. segment_count - 1 ,
157158 reader,
158- } )
159+ head : None ,
160+ } ;
161+ let _ = cursor. next ( ) ?;
162+ Ok ( cursor)
163+ }
164+
165+ fn peek ( & self ) -> Option < & SortRow > {
166+ self . head . as_ref ( )
159167 }
160168
161169 fn next ( & mut self ) -> Result < Option < SortRow > , DatabaseError > {
170+ let head = self . head . take ( ) ;
162171 loop {
163172 if let Some ( row) = self . reader . next ( ) {
164- return row. map ( Some ) ;
173+ self . head = Some ( row?) ;
174+ return Ok ( head) ;
165175 }
166176 if self . remaining_segments == 0 {
167- return Ok ( None ) ;
177+ return Ok ( head ) ;
168178 }
169179 if !self . reader . start_next_segment ( ) ? {
170180 return Err ( DatabaseError :: InvalidValue (
@@ -200,34 +210,29 @@ fn merge_pass<'on_flush>(
200210 let mut target = SpillVec :: new ( ) ;
201211 target_runs. clear ( ) ;
202212 target_runs. reserve ( source_runs. len ( ) . div_ceil ( fan_in) ) ;
203- let mut cursors = Vec :: with_capacity ( fan_in) ;
204213 // Perf: a loser tree or binary heap would reduce head selection from O(K) to O(log K), but
205214 // K is deliberately small, so a linear scan keeps the merge state and update path simpler.
206- let mut heads = Vec :: with_capacity ( fan_in) ;
215+ let mut cursors = Vec :: with_capacity ( fan_in) ;
207216
208217 for run_group in source_runs. chunks ( fan_in) {
209- heads. clear ( ) ;
210218 cursors. clear ( ) ;
211219 for run in run_group {
212220 cursors. push ( RunCursor :: new ( & source, run) ?) ;
213221 }
214- for cursor in & mut cursors {
215- heads. push ( cursor. next ( ) ?) ;
216- }
217222 let mut first_segment = None ;
218223 let mut segment_count = 0 ;
219224
220225 loop {
221226 let mut selected = None ;
222- for ( index, row ) in heads . iter ( ) . enumerate ( ) {
223- let Some ( row) = row else {
227+ for ( index, cursor ) in cursors . iter ( ) . enumerate ( ) {
228+ let Some ( row) = cursor . peek ( ) else {
224229 continue ;
225230 } ;
226231 let Some ( current) = selected else {
227232 selected = Some ( index) ;
228233 continue ;
229234 } ;
230- let Some ( current_row) = heads [ current] . as_ref ( ) else {
235+ let Some ( current_row) = cursors [ current] . peek ( ) else {
231236 selected = Some ( index) ;
232237 continue ;
233238 } ;
@@ -244,7 +249,7 @@ fn merge_pass<'on_flush>(
244249 let Some ( selected) = selected else {
245250 break ;
246251 } ;
247- let Some ( row) = heads [ selected] . take ( ) else {
252+ let Some ( row) = cursors [ selected] . next ( ) ? else {
248253 return Err ( DatabaseError :: InvalidValue (
249254 "sort merge selected an empty run" . to_string ( ) ,
250255 ) ) ;
@@ -253,7 +258,6 @@ fn merge_pass<'on_flush>(
253258 first_segment. get_or_insert ( segment) ;
254259 segment_count += 1 ;
255260 }
256- heads[ selected] = cursors[ selected] . next ( ) ?;
257261 }
258262
259263 if let Some ( segment) = target. flush ( ) ? {
0 commit comments