| 172 | type SortKeyOffsets = Vec<Option<(usize, usize)>>; |
| 173 | |
| 174 | pub(in crate::data::executor) fn sort_rows( |
| 175 | rows: &mut [(String, Vec<u8>)], |
| 176 | sort_keys: &[(String, bool)], |
| 177 | ) { |
| 178 | if sort_keys.is_empty() { |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | // Pre-extract sort key offsets for all rows — one scan per row instead |
| 183 | // of O(N log N) scans during comparisons. |
| 184 | let mut key_offsets: Vec<SortKeyOffsets> = rows |
| 185 | .iter() |
| 186 | .map(|(_, bytes)| { |
| 187 | sort_keys |
| 188 | .iter() |
| 189 | .map(|(field, _)| msgpack_scan::extract_field(bytes, 0, field)) |
| 190 | .collect() |
| 191 | }) |
| 192 | .collect(); |
| 193 | |
| 194 | // Sort indices using pre-extracted offsets. |
| 195 | let mut indices: Vec<usize> = (0..rows.len()).collect(); |
| 196 | indices.sort_by(|&ai, &bi| { |
| 197 | compare_with_preextracted( |
| 198 | &rows[ai].1, |
| 199 | &key_offsets[ai], |
| 200 | &rows[bi].1, |
| 201 | &key_offsets[bi], |
| 202 | sort_keys, |
| 203 | ) |
| 204 | }); |
| 205 | |
| 206 | // Apply permutation in-place. |
| 207 | apply_permutation(rows, &mut key_offsets, indices); |
| 208 | } |
| 209 | |
| 210 | /// Compare two docs using pre-extracted sort key offsets. |
| 211 | fn compare_with_preextracted( |