| 3291 | } |
| 3292 | |
| 3293 | fn scan_new_bytes(&mut self, data: &[u8]) { |
| 3294 | if self.scan_pos >= data.len() { |
| 3295 | return; |
| 3296 | } |
| 3297 | |
| 3298 | if let Some(csv) = self.csv.as_mut() { |
| 3299 | let mut input = &data[self.scan_pos..]; |
| 3300 | let mut consumed = 0usize; |
| 3301 | while !input.is_empty() { |
| 3302 | let (result, n_input, _n_output, _n_ends) = |
| 3303 | csv.reader |
| 3304 | .read_record(input, &mut csv.output, &mut csv.ends); |
| 3305 | consumed += n_input; |
| 3306 | input = &input[n_input..]; |
| 3307 | |
| 3308 | match result { |
| 3309 | ReadRecordResult::InputEmpty => break, |
| 3310 | ReadRecordResult::OutputFull => { |
| 3311 | if n_input == 0 { |
| 3312 | csv.output |
| 3313 | .resize(csv.output.len().saturating_mul(2).max(1), 0); |
| 3314 | } |
| 3315 | } |
| 3316 | ReadRecordResult::OutputEndsFull => { |
| 3317 | if n_input == 0 { |
| 3318 | csv.ends.resize(csv.ends.len().saturating_mul(2).max(1), 0); |
| 3319 | } |
| 3320 | } |
| 3321 | ReadRecordResult::Record | ReadRecordResult::End => { |
| 3322 | let row_end = self.scan_pos + consumed; |
| 3323 | self.last_row_end = Some(row_end); |
| 3324 | if self.end_marker_end.is_none() { |
| 3325 | let is_marker = if csv.skip_first_record { |
| 3326 | csv.skip_first_record = false; |
| 3327 | false |
| 3328 | } else { |
| 3329 | // Detect the marker against the raw input |
| 3330 | // bytes, not the CSV-decoded record. A quoted |
| 3331 | // data row `"\."` decodes to `\.` but must be |
| 3332 | // imported as data; only a bare `\.` line |
| 3333 | // terminates the COPY. |
| 3334 | let raw = &data[self.record_start..row_end]; |
| 3335 | // csv-core ends a CRLF record after the `\r`, |
| 3336 | // leaving the trailing `\n` as the leading byte |
| 3337 | // of the next record's span; a CR-only record |
| 3338 | // ends in a lone `\r`. So a `\.` marker record's |
| 3339 | // raw span can be `\.\n` (LF), `\n\.\r` (CRLF) |
| 3340 | // or `\.\r` (CR). Trim CR/LF from both ends |
| 3341 | // before comparing — a trailing-only strip would |
| 3342 | // miss the CRLF/CR forms. Quoted `"\."` data |
| 3343 | // keeps its surrounding quotes after trimming and |
| 3344 | // is therefore correctly rejected. |
| 3345 | let start = raw |
| 3346 | .iter() |
| 3347 | .take_while(|&&b| b == b'\r' || b == b'\n') |
| 3348 | .count(); |
| 3349 | let trailing = raw[start..] |
| 3350 | .iter() |