Skip to main content

csv/
reader.rs

1#[cfg(feature = "serde")]
2use alloc::collections::BTreeMap;
3#[cfg(feature = "serde")]
4use alloc::sync::Arc;
5use alloc::{
6    string::{String, ToString},
7    vec::Vec,
8};
9use core::fmt;
10
11use crate::error::{ReadError, ReadErrorKind};
12
13/// Custom read abstraction that allows `Reader` to work without `std::io::Read`.
14///
15/// This trait is automatically implemented for `&[u8]` and, when the `std`
16/// feature is enabled, for any `std::io::Read` type.
17pub trait Read {
18    /// Read data into `buf`, appending to its contents.
19    ///
20    /// Returns `Ok(true)` when at least one byte was appended to `buf`, or
21    /// `Ok(false)` to signal end-of-input.
22    ///
23    /// Implementations **must not** return `Ok(true)` without appending at
24    /// least one byte. Doing so is a contract violation; rather than spinning
25    /// or panicking, the [`Reader`] reports [`ReadErrorKind::NoProgress`] and
26    /// stops reading.
27    fn read(&mut self, buf: &mut Vec<u8>) -> Result<bool, ReadError>;
28}
29
30#[cfg(not(feature = "std"))]
31impl Read for &[u8] {
32    fn read(&mut self, buf: &mut Vec<u8>) -> Result<bool, ReadError> {
33        let chunk = 16384;
34        let to_copy = chunk.min(self.len());
35        if to_copy == 0 {
36            return Ok(false);
37        }
38        buf.extend_from_slice(&self[..to_copy]);
39        *self = &self[to_copy..];
40        Ok(true)
41    }
42}
43
44#[cfg(feature = "std")]
45impl<R: std::io::Read> Read for R {
46    fn read(&mut self, buf: &mut Vec<u8>) -> Result<bool, ReadError> {
47        let mut tmp = [0u8; 16384];
48        let n = std::io::Read::read(self, &mut tmp)?;
49        if n == 0 {
50            return Ok(false);
51        }
52        buf.extend_from_slice(&tmp[..n]);
53        Ok(true)
54    }
55}
56
57#[derive(Clone, Copy, Debug, PartialEq)]
58enum State {
59    StartOfField,
60    InUnquoted,
61    InQuoted,
62    AfterQuote,
63}
64
65#[derive(Clone, Copy, Debug)]
66enum Src {
67    Buf,
68    Scratch,
69}
70
71#[derive(Clone, Copy, Debug)]
72struct RawField {
73    start: usize,
74    end: usize,
75    src: Src,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq)]
79pub struct FieldRange {
80    pub start: usize,
81    pub end: usize,
82}
83
84/// A pull-based CSV reader that parses records from any [`Read`] source.
85///
86/// The reader supports both in-memory slices (`&[u8]`) and streaming I/O sources
87/// (`std::io::Read`) via the custom [`Read`] trait. Rows are parsed on-demand
88/// by calling [`rows`](Self::rows) or [`rows_bytes`](Self::rows_bytes).
89///
90/// # Configuration
91///
92/// Use builder methods for a fluent setup:
93/// - [`set_delimiter`](Self::set_delimiter) — change the field separator (default `,`)
94/// - [`set_flexible`](Self::set_flexible) — allow variable-length rows (default `false`)
95/// - [`set_headers`](Self::set_headers) — supply column names without parsing
96/// - [`set_max_record_bytes`](Self::set_max_record_bytes),
97///   [`set_max_field_bytes`](Self::set_max_field_bytes) and
98///   [`set_max_columns`](Self::set_max_columns) — opt-in resource limits
99///
100/// # Headers
101///
102/// Call [`parse_headers`](Self::parse_headers) to treat the first row as column
103/// names. After doing so, [`headers`](Self::headers) returns a `Some` slice and
104/// serde deserialization (with the `serde` feature) can match by field name.
105///
106/// # Errors
107///
108/// Parsing errors (unterminated quotes, trailing content after a quoted field,
109/// inconsistent field counts) are stored on the returned row and surfaced when
110/// the row's fields are accessed. I/O errors are also stored on the row. This
111/// allows iteration to continue past malformed rows: when a record is
112/// malformed, the reader skips to the next record boundary before parsing the
113/// following record.
114///
115/// Errors are reported against the line on which the offending record started.
116///
117/// # Resource limits
118///
119/// By default the reader imposes no size limits, so a single enormous record
120/// can consume an unbounded amount of memory. Applications that parse
121/// untrusted input should set [`set_max_record_bytes`](Self::set_max_record_bytes)
122/// (and optionally [`set_max_field_bytes`](Self::set_max_field_bytes) /
123/// [`set_max_columns`](Self::set_max_columns)); exceeding a limit produces an
124/// error on the row instead of exhausting memory.
125///
126/// # Iteration
127///
128/// Produces owned [`Row`] or [`BytesRow`] values. A [`Row`] validates UTF-8
129/// on field access; [`BytesRow`] does not. Rows can outlive the reader.
130///
131/// # Examples
132///
133/// ```rust
134/// use csv::Reader;
135///
136/// let data = b"name,age\nAlice,30\nBob,25\n";
137/// let mut reader = Reader::new(&data[..]);
138///
139/// // Parse the first row as headers.
140/// let headers = reader.parse_headers().unwrap();
141/// assert_eq!(headers, &["name", "age"]);
142///
143/// // Iterate over data rows.
144/// for row in reader.rows() {
145///     for field in row.iter() {
146///         let _: &str = field.unwrap();
147///     }
148/// }
149/// ```
150///
151/// ```no_run
152/// # #[cfg(feature = "std")] {
153/// # use csv::Reader;
154/// let file = std::fs::File::open("data.csv").unwrap();
155/// let mut reader = Reader::new(file).set_delimiter(b';');
156///
157/// for row in reader.rows() {
158///     let name = row.get(0).unwrap().unwrap();
159///     println!("{name}");
160/// }
161/// # }
162/// ```
163pub struct Reader<R: Read> {
164    /// Raw input data read from source.
165    buf: Vec<u8>,
166    /// Temporary storage for quoted field content (unescaped).
167    scratch: Vec<u8>,
168    /// Field ranges into `buf` or `scratch` for the current row.
169    ranges: Vec<RawField>,
170    /// Start of unconsumed data in `buf`.
171    start: usize,
172    /// End of valid data in `buf`.
173    end: usize,
174    /// Number of bytes dropped from the front of `buf` by [`Reader::compact`].
175    ///
176    /// The absolute stream offset of `buf[start]` is `discarded + start`.
177    discarded: usize,
178    /// Absolute stream offset at which the current record started.
179    record_start: usize,
180    /// Physical line number (1-based) of the byte at `buf[start]`.
181    line: usize,
182    /// Physical line number on which the current record started.
183    record_line: usize,
184    /// Start of current unquoted field within `buf`.
185    field_start: usize,
186    /// Start of current quoted field within `scratch`.
187    scratch_field_start: usize,
188    /// Current parser state machine position.
189    state: State,
190    /// Field delimiter byte (default `,`).
191    delimiter: u8,
192    /// If true, rows may have varying field counts.
193    flexible: bool,
194    /// Expected field count per row, inferred from the first data row.
195    num_fields: Option<usize>,
196    /// True once `parse_headers` or `set_headers` has been called.
197    headers_parsed: bool,
198    /// A bare `\r` was the last byte of the previous buffer; next `\n` should be skipped.
199    pending_cr: bool,
200    /// The previous quoted-content slice ended with a bare `\r`; a leading `\n`
201    /// in the next slice continues that line break and must not be counted twice.
202    pending_lf_quoted: bool,
203    /// True once the leading byte-order mark has been considered.
204    bom_checked: bool,
205    /// If true, a UTF-8 byte-order mark at the very start of the input is skipped.
206    strip_bom: bool,
207    /// Maximum accepted size of a single record, in bytes (opt-in).
208    max_record_bytes: Option<usize>,
209    /// Maximum accepted size of a single field, in bytes (opt-in).
210    max_field_bytes: Option<usize>,
211    /// Maximum accepted number of columns per record (opt-in).
212    max_columns: Option<usize>,
213    /// Column names, populated by `parse_headers` or `set_headers`.
214    headers: Vec<String>,
215    #[cfg(feature = "serde")]
216    /// Map from header name -> column index for serde field lookup.
217    header_map: Option<Arc<BTreeMap<String, usize>>>,
218    #[cfg(feature = "serde")]
219    /// True when `headers` contains duplicate names, which makes name-based
220    /// deserialization ambiguous.
221    duplicate_headers: bool,
222    /// The underlying data source.
223    source: R,
224    /// True when the source has signalled end-of-input.
225    eof: bool,
226}
227
228// ── Common methods (always available) ─────────────────────────────────
229
230impl<R: Read> Reader<R> {
231    /// Creates a new `Reader` from any [`Read`] source.
232    ///
233    /// This works with `&[u8]`, `std::io::Cursor`, `std::fs::File`, etc.
234    pub fn new(source: R) -> Self {
235        Reader {
236            buf: Vec::with_capacity(65536),
237            scratch: Vec::new(),
238            ranges: Vec::new(),
239            start: 0,
240            end: 0,
241            discarded: 0,
242            record_start: 0,
243            line: 1,
244            record_line: 1,
245            field_start: 0,
246            scratch_field_start: 0,
247            state: State::StartOfField,
248            delimiter: b',',
249            flexible: false,
250            num_fields: None,
251            headers_parsed: false,
252            pending_cr: false,
253            pending_lf_quoted: false,
254            bom_checked: false,
255            strip_bom: true,
256            max_record_bytes: None,
257            max_field_bytes: None,
258            max_columns: None,
259            headers: Vec::new(),
260            #[cfg(feature = "serde")]
261            header_map: None,
262            #[cfg(feature = "serde")]
263            duplicate_headers: false,
264            source,
265            eof: false,
266        }
267    }
268
269    /// Sets the field delimiter byte (default is `,`).
270    ///
271    /// # Panics
272    ///
273    /// Panics if `byte` is `"`, `\r` or `\n`. These bytes carry structural
274    /// meaning in CSV and cannot be used as a delimiter: `"` would disable
275    /// quoting, and `\r`/`\n` would make record boundaries unrepresentable,
276    /// causing the whole input to be read as a single record.
277    pub fn set_delimiter(mut self, byte: u8) -> Self {
278        assert!(
279            byte != b'"' && byte != b'\r' && byte != b'\n',
280            "invalid CSV delimiter {byte:#04x}: `\"`, `\\r` and `\\n` are not allowed"
281        );
282        self.delimiter = byte;
283        self
284    }
285
286    /// Sets whether variable field counts are allowed (default is `false`).
287    pub fn set_flexible(mut self, yes: bool) -> Self {
288        self.flexible = yes;
289        self
290    }
291
292    /// Controls whether a UTF-8 byte-order mark (`U+FEFF`, the bytes
293    /// `EF BB BF`) at the very start of the input is skipped (default is
294    /// `true`).
295    ///
296    /// This is what makes files produced by Excel, which writes UTF-8 CSV with
297    /// a BOM by default, work with [`parse_headers`](Self::parse_headers) and
298    /// serde: without it the first header would be `"\u{feff}name"` and would
299    /// match no struct field.
300    ///
301    /// Only the first three bytes of the stream are considered, and only once.
302    /// A BOM anywhere else in the input is always treated as data.
303    ///
304    /// Pass `false` to keep the mark as part of the first field.
305    pub fn set_strip_bom(mut self, yes: bool) -> Self {
306        self.strip_bom = yes;
307        self
308    }
309
310    /// Rejects any record longer than `max` bytes.
311    ///
312    /// This is the main defence against a single crafted record exhausting
313    /// memory. When a record exceeds the limit the row carries a
314    /// [`ReadErrorKind::RecordTooLarge`] error and reading stops, because the
315    /// remainder of the oversized record cannot be skipped safely.
316    ///
317    /// By default no limit is applied.
318    pub fn set_max_record_bytes(mut self, max: usize) -> Self {
319        self.max_record_bytes = Some(max);
320        self
321    }
322
323    /// Rejects any field longer than `max` bytes.
324    ///
325    /// When a field exceeds the limit the row carries a
326    /// [`ReadErrorKind::FieldTooLarge`] error and **reading stops**, because the
327    /// remainder of the oversized record cannot be skipped without reading it.
328    ///
329    /// By default no limit is applied.
330    pub fn set_max_field_bytes(mut self, max: usize) -> Self {
331        self.max_field_bytes = Some(max);
332        self
333    }
334
335    /// Rejects any record with more than `max` columns.
336    ///
337    /// Each column costs at least one byte of input, so this is a cheap way to
338    /// bound the bookkeeping memory used by a record made of millions of empty
339    /// fields. When a record exceeds the limit the row carries a
340    /// [`ReadErrorKind::TooManyColumns`] error and **reading stops**, because
341    /// the remainder of the oversized record cannot be skipped without reading
342    /// it.
343    ///
344    /// By default no limit is applied.
345    pub fn set_max_columns(mut self, max: usize) -> Self {
346        self.max_columns = Some(max);
347        self
348    }
349
350    /// Sets the column names for header-based serde deserialization.
351    ///
352    /// Calling this marks headers as parsed so `reader.headers()` returns `Some`,
353    /// and pins the expected field count to the number of names supplied (unless
354    /// [`set_flexible`](Self::set_flexible) is enabled), exactly like
355    /// [`parse_headers`](Self::parse_headers).
356    pub fn set_headers(mut self, headers: Vec<String>) -> Self {
357        self.headers_parsed = true;
358        if !headers.is_empty() {
359            self.num_fields = Some(headers.len());
360        }
361        self.headers = headers;
362        self.rebuild_header_map();
363        self
364    }
365
366    #[cfg(feature = "serde")]
367    fn rebuild_header_map(&mut self) {
368        let map: BTreeMap<String, usize> = self
369            .headers
370            .iter()
371            .enumerate()
372            .map(|(i, name)| (name.clone(), i))
373            .collect();
374        self.duplicate_headers = map.len() != self.headers.len();
375        self.header_map = Some(Arc::new(map));
376    }
377
378    #[cfg(not(feature = "serde"))]
379    fn rebuild_header_map(&mut self) {}
380
381    /// Returns `Some(&[String])` if headers were parsed or set, `None` otherwise.
382    pub fn headers(&self) -> Option<&[String]> {
383        if self.headers_parsed { Some(&self.headers) } else { None }
384    }
385
386    /// Parses the first row as column headers and stores them internally.
387    ///
388    /// Returns the header strings as a slice, and pins the expected field count
389    /// to the number of headers (unless [`set_flexible`](Self::set_flexible) is
390    /// enabled).
391    ///
392    /// If the input is empty this returns an empty slice and leaves
393    /// [`headers`](Self::headers) as `None`, since no header row was seen.
394    pub fn parse_headers(&mut self) -> Result<&[String], ReadError> {
395        let row = match self.read_row() {
396            Some(row) => row,
397            None => return Ok(&[]),
398        };
399        let headers: Vec<String> = row.to_vec()?.iter().map(|s| s.to_string()).collect();
400        self.headers_parsed = true;
401        self.num_fields = Some(headers.len());
402        self.headers = headers;
403        self.rebuild_header_map();
404        Ok(&self.headers)
405    }
406
407    /// Returns an iterator over `Row` values (validated as UTF-8).
408    pub fn rows(&mut self) -> Rows<'_, R> {
409        Rows {
410            reader: self,
411        }
412    }
413
414    /// Returns an iterator over `BytesRow` values (no UTF-8 validation).
415    pub fn rows_bytes(&mut self) -> BytesRows<'_, R> {
416        BytesRows {
417            reader: self,
418        }
419    }
420
421    /// Reads more data from the source into `buf`, updating `self.end`.
422    ///
423    /// Returns `Ok(true)` if new data was loaded, `Ok(false)` at EOF, or `Err` on
424    /// I/O error or when the source violates the [`Read`] contract by reporting
425    /// progress without appending any bytes.
426    ///
427    /// Once EOF is reached, subsequent calls return `Ok(false)` without re-reading.
428    fn fill_buf(&mut self) -> Result<bool, ReadError> {
429        if self.eof {
430            return Ok(false);
431        }
432        let before = self.buf.len();
433        if self.source.read(&mut self.buf)? {
434            if self.buf.len() == before {
435                // Contract violation: `Ok(true)` without progress. Report it
436                // instead of looping forever or indexing out of bounds.
437                self.eof = true;
438                return Err(ReadError::new(ReadErrorKind::NoProgress, self.record_line, 0));
439            }
440            self.end = self.buf.len();
441            Ok(true)
442        } else {
443            self.eof = true;
444            Ok(false)
445        }
446    }
447
448    /// Skips a UTF-8 byte-order mark at the very start of the input.
449    ///
450    /// The decision needs up to three bytes, so this may read ahead before the
451    /// first record is parsed. That is necessary: with a byte-at-a-time source
452    /// the leading `0xEF` would otherwise be consumed as field content before
453    /// the mark could be recognised.
454    fn strip_leading_bom(&mut self) -> Result<(), ReadError> {
455        if self.bom_checked {
456            return Ok(());
457        }
458        self.bom_checked = true;
459        if !self.strip_bom {
460            return Ok(());
461        }
462        while self.end - self.start < 3 && !self.eof {
463            self.fill_buf()?;
464        }
465        if self.buf[self.start..].starts_with(&crate::BOM) {
466            self.start += 3;
467        }
468        Ok(())
469    }
470
471    /// Parses one CSV row from the input, or returns `None` at EOF.
472    ///
473    /// The row's fields are assembled into an owned `Row`. Errors (I/O,
474    /// unterminated quotes, trailing content, inconsistent field counts,
475    /// configured size limits) are stored on the `Row` and deferred until
476    /// access.
477    fn read_row(&mut self) -> Option<Row> {
478        self.compact();
479        self.ranges.clear();
480        self.scratch.clear();
481        self.state = State::StartOfField;
482        self.pending_lf_quoted = false;
483        self.record_line = self.line;
484
485        if let Err(e) = self.strip_leading_bom() {
486            self.eof = true;
487            return Some(self.build_row(Some(e)));
488        }
489
490        self.record_start = self.discarded + self.start;
491
492        loop {
493            if self.start >= self.end {
494                match self.fill_buf() {
495                    Err(e) => {
496                        self.eof = true;
497                        return match self.state {
498                            State::InQuoted => Some(self.build_row(Some(e))),
499                            _ => {
500                                self.finalize_current_field();
501                                Some(self.build_row(Some(e)))
502                            }
503                        };
504                    }
505                    Ok(false) => {
506                        if self.ranges.is_empty() && self.state == State::StartOfField {
507                            return None;
508                        }
509                        return match self.state {
510                            State::InQuoted => Some(self.build_row(Some(ReadError::new(
511                                ReadErrorKind::UnterminatedQuote,
512                                self.record_line,
513                                0,
514                            )))),
515                            _ => {
516                                self.finalize_current_field();
517                                Some(self.build_row(None))
518                            }
519                        };
520                    }
521                    Ok(true) => {}
522                }
523            }
524
525            // Enforce the opt-in resource limits before touching the byte, so a
526            // malicious record cannot grow the buffers any further.
527            if let Some(kind) = self.limit_exceeded() {
528                return Some(self.abandon_record(kind));
529            }
530
531            if self.pending_cr {
532                if self.buf[self.start] == b'\n' {
533                    self.start += 1;
534                }
535                self.pending_cr = false;
536                continue;
537            }
538
539            let byte = self.buf[self.start];
540
541            match self.state {
542                State::StartOfField => {
543                    if byte == b'\r' || byte == b'\n' {
544                        if !self.ranges.is_empty() {
545                            self.ranges.push(RawField {
546                                start: self.start,
547                                end: self.start,
548                                src: Src::Buf,
549                            });
550                        }
551                        self.consume_newline();
552                        if self.ranges.is_empty() {
553                            continue;
554                        }
555                        return Some(self.build_row(None));
556                    }
557                    self.field_start = self.start;
558                    if byte == self.delimiter {
559                        self.ranges.push(RawField {
560                            start: self.start,
561                            end: self.start,
562                            src: Src::Buf,
563                        });
564                        self.start += 1;
565                        continue;
566                    }
567                    if byte == b'"' {
568                        self.scratch_field_start = self.scratch.len();
569                        self.start += 1;
570                        self.state = State::InQuoted;
571                    } else {
572                        self.start += 1;
573                        self.state = State::InUnquoted;
574                    }
575                }
576
577                State::InUnquoted => {
578                    let haystack = &self.buf[self.start..self.end];
579                    match memchr::memchr3(self.delimiter, b'\r', b'\n', haystack) {
580                        Some(offset) => {
581                            let pos = self.start + offset;
582                            if let Some(kind) = self.field_limit_exceeded(pos - self.field_start) {
583                                return Some(self.abandon_record(kind));
584                            }
585                            self.ranges.push(RawField {
586                                start: self.field_start,
587                                end: pos,
588                                src: Src::Buf,
589                            });
590                            let b = self.buf[pos];
591                            if b == self.delimiter {
592                                self.start = pos + 1;
593                                self.state = State::StartOfField;
594                            } else {
595                                self.start = pos;
596                                self.consume_newline();
597                                return Some(self.build_row(None));
598                            }
599                        }
600                        None => {
601                            self.start = self.end;
602                            if let Some(kind) = self.field_limit_exceeded(self.end - self.field_start) {
603                                return Some(self.abandon_record(kind));
604                            }
605                        }
606                    }
607                }
608
609                State::InQuoted => {
610                    let haystack = &self.buf[self.start..self.end];
611                    match memchr::memchr(b'"', haystack) {
612                        Some(offset) => {
613                            let quote_pos = self.start + offset;
614                            let segment = &self.buf[self.start..quote_pos];
615                            // Check before copying into `scratch`, so the limit
616                            // bounds the scratch buffer too.
617                            if let Some(kind) =
618                                self.field_limit_exceeded(self.scratch.len() - self.scratch_field_start + segment.len())
619                            {
620                                return Some(self.abandon_record(kind));
621                            }
622                            self.line += count_line_breaks(segment, &mut self.pending_lf_quoted);
623                            // A `"` follows, so a trailing `\r` cannot pair with
624                            // anything and has already been counted.
625                            self.pending_lf_quoted = false;
626                            self.scratch.extend_from_slice(segment);
627                            let after_quote = quote_pos + 1;
628                            if after_quote < self.end && self.buf[after_quote] == b'"' {
629                                self.scratch.push(b'"');
630                                self.start = after_quote + 1;
631                            } else if after_quote < self.end {
632                                self.ranges.push(RawField {
633                                    start: self.scratch_field_start,
634                                    end: self.scratch.len(),
635                                    src: Src::Scratch,
636                                });
637                                self.start = after_quote;
638                                self.state = State::AfterQuote;
639                            } else {
640                                match self.fill_buf() {
641                                    Ok(true) if self.buf.get(after_quote) == Some(&b'"') => {
642                                        self.scratch.push(b'"');
643                                        self.start = after_quote + 1;
644                                    }
645                                    Err(e) => {
646                                        self.ranges.push(RawField {
647                                            start: self.scratch_field_start,
648                                            end: self.scratch.len(),
649                                            src: Src::Scratch,
650                                        });
651                                        self.start = after_quote;
652                                        self.state = State::AfterQuote;
653                                        return Some(self.build_row(Some(e)));
654                                    }
655                                    _ => {
656                                        self.ranges.push(RawField {
657                                            start: self.scratch_field_start,
658                                            end: self.scratch.len(),
659                                            src: Src::Scratch,
660                                        });
661                                        self.start = after_quote;
662                                        self.state = State::AfterQuote;
663                                    }
664                                }
665                            }
666                        }
667                        None => {
668                            self.line +=
669                                count_line_breaks(&self.buf[self.start..self.end], &mut self.pending_lf_quoted);
670                            self.scratch.extend_from_slice(&self.buf[self.start..self.end]);
671                            self.start = self.end;
672                            if let Some(kind) = self.field_limit_exceeded(self.scratch.len() - self.scratch_field_start)
673                            {
674                                return Some(self.abandon_record(kind));
675                            }
676                        }
677                    }
678                }
679
680                State::AfterQuote => {
681                    if byte == self.delimiter {
682                        self.start += 1;
683                        self.state = State::StartOfField;
684                    } else if is_newline(byte) {
685                        self.consume_newline();
686                        return Some(self.build_row(None));
687                    } else {
688                        return Some(self.abandon_record(ReadErrorKind::TrailingContent));
689                    }
690                }
691            }
692        }
693    }
694
695    /// Returns the first configured limit the current record has exceeded.
696    ///
697    /// Field sizes are checked where a field is resolved (see
698    /// [`field_limit_exceeded`](Self::field_limit_exceeded)); this covers the
699    /// record and column limits, which grow monotonically as parsing advances.
700    fn limit_exceeded(&self) -> Option<ReadErrorKind> {
701        if let Some(limit) = self.max_record_bytes {
702            let consumed = self.discarded + self.start - self.record_start;
703            if consumed > limit {
704                return Some(ReadErrorKind::RecordTooLarge {
705                    limit,
706                });
707            }
708        }
709        if let Some(limit) = self.max_columns
710            && self.ranges.len() > limit
711        {
712            return Some(ReadErrorKind::TooManyColumns {
713                limit,
714            });
715        }
716        None
717    }
718
719    /// Returns [`ReadErrorKind::FieldTooLarge`] if a field of `len` bytes
720    /// exceeds the configured field limit.
721    fn field_limit_exceeded(&self, len: usize) -> Option<ReadErrorKind> {
722        match self.max_field_bytes {
723            Some(limit) if len > limit => Some(ReadErrorKind::FieldTooLarge {
724                limit,
725            }),
726            _ => None,
727        }
728    }
729
730    /// Records the fields parsed so far and abandons the current record,
731    /// positioning the reader at the start of the next record where possible.
732    fn abandon_record(&mut self, kind: ReadErrorKind) -> Row {
733        let error = ReadError::new(kind, self.record_line, 0);
734        match error.kind {
735            // A configured limit was exceeded. Reading further would defeat it
736            // (the remainder of the record could be arbitrarily long), so drop
737            // the rest of the buffer and stop.
738            ReadErrorKind::RecordTooLarge {
739                ..
740            }
741            | ReadErrorKind::FieldTooLarge {
742                ..
743            }
744            | ReadErrorKind::TooManyColumns {
745                ..
746            } => {
747                self.start = self.end;
748                self.eof = true;
749            }
750            _ => {
751                // Keep the fields parsed before the offending byte, then skip
752                // the junk so the next record starts at a record boundary.
753                self.finalize_current_field();
754                if !self.skip_record_tail() {
755                    self.eof = true;
756                }
757            }
758        }
759        self.build_row(Some(error))
760    }
761
762    /// Consumes input up to and including the next record terminator.
763    ///
764    /// Returns `false` if the skip could not complete (end of input, I/O error,
765    /// or the configured record-size limit was reached), in which case the
766    /// reader is left at end-of-input.
767    fn skip_record_tail(&mut self) -> bool {
768        loop {
769            if self.start >= self.end {
770                match self.fill_buf() {
771                    Ok(true) => {}
772                    _ => return false,
773                }
774            }
775            if self.pending_cr {
776                if self.buf[self.start] == b'\n' {
777                    self.start += 1;
778                }
779                self.pending_cr = false;
780                return true;
781            }
782            let haystack = &self.buf[self.start..self.end];
783            match memchr::memchr2(b'\r', b'\n', haystack) {
784                Some(offset) => {
785                    self.start += offset;
786                    self.consume_newline();
787                    return true;
788                }
789                None => {
790                    self.start = self.end;
791                    if let Some(limit) = self.max_record_bytes
792                        && self.discarded + self.start - self.record_start > limit
793                    {
794                        return false;
795                    }
796                }
797            }
798        }
799    }
800
801    /// Moves unconsumed data to the front of `buf` and truncates.
802    ///
803    /// Compaction is a full copy of the unconsumed tail, so it is only done
804    /// once a meaningful amount of data has been consumed. Compacting on every
805    /// record would memmove the whole remaining buffer per record, which
806    /// dominates runtime for inputs made of small records.
807    ///
808    /// Only ever called at a record boundary, where no parsed field ranges are
809    /// live.
810    fn compact(&mut self) {
811        if self.start == 0 {
812            return;
813        }
814        if self.start < 4096 && self.start * 2 < self.buf.len() {
815            return;
816        }
817        let shift = self.start;
818        let remaining = self.end - shift;
819        if remaining > 0 {
820            self.buf.copy_within(shift..self.end, 0);
821        }
822        self.buf.truncate(remaining);
823        self.end = remaining;
824        self.start = 0;
825        self.discarded += shift;
826
827        // Give back memory retained from an unusually large record. This only
828        // triggers once the buffer has grown far beyond its initial capacity,
829        // so it never affects steady-state throughput.
830        const RETAINED_CAPACITY: usize = 1 << 20;
831        if self.buf.capacity() > RETAINED_CAPACITY && self.end <= RETAINED_CAPACITY {
832            self.buf.shrink_to_fit();
833        }
834    }
835
836    /// Consumes a `\r\n`, `\n`, or `\r` line ending, incrementing the line counter.
837    ///
838    /// If a bare `\r` is at the end of the buffer, sets `pending_cr` so the
839    /// following `\n` is skipped on the next read.
840    fn consume_newline(&mut self) {
841        if self.start < self.end && self.buf[self.start] == b'\r' {
842            self.start += 1;
843            if self.start >= self.end {
844                self.pending_cr = true;
845            }
846        }
847        if self.start < self.end && self.buf[self.start] == b'\n' {
848            self.start += 1;
849        }
850        self.line += 1;
851    }
852
853    /// Pushes a `RawField` entry for the current unquoted or empty field.
854    ///
855    /// Does nothing in the `AfterQuote` state (the field is already recorded).
856    fn finalize_current_field(&mut self) {
857        match self.state {
858            State::InUnquoted => {
859                self.ranges.push(RawField {
860                    start: self.field_start,
861                    end: self.start,
862                    src: Src::Buf,
863                });
864            }
865            State::InQuoted => {
866                self.ranges.push(RawField {
867                    start: self.scratch_field_start,
868                    end: self.scratch.len(),
869                    src: Src::Scratch,
870                });
871            }
872            State::AfterQuote => {}
873            State::StartOfField => {
874                if !self.ranges.is_empty() {
875                    self.ranges.push(RawField {
876                        start: self.start,
877                        end: self.start,
878                        src: Src::Buf,
879                    });
880                }
881            }
882        }
883    }
884
885    /// Wraps the bytes row into a `Row`, attaching the header map if available.
886    fn build_row(&mut self, error: Option<ReadError>) -> Row {
887        let bytes_row = self.build_bytes_row(error);
888        Row {
889            inner: bytes_row,
890            #[cfg(feature = "serde")]
891            header_map: self.header_map.clone(),
892            #[cfg(feature = "serde")]
893            duplicate_headers: self.duplicate_headers,
894        }
895    }
896
897    /// Assembles the parsed fields into an owned `BytesRow`, checking field-count consistency.
898    fn build_bytes_row(&mut self, error: Option<ReadError>) -> BytesRow {
899        let mut total: usize = 0;
900        for r in &self.ranges {
901            total += r.end - r.start;
902        }
903
904        let mut row_buf = Vec::with_capacity(total);
905        let mut row_ranges = Vec::with_capacity(self.ranges.len());
906
907        for r in &self.ranges {
908            let slice = match r.src {
909                Src::Buf => &self.buf[r.start..r.end],
910                Src::Scratch => &self.scratch[r.start..r.end],
911            };
912            let start = row_buf.len();
913            row_buf.extend_from_slice(slice);
914            let end = row_buf.len();
915            row_ranges.push(FieldRange {
916                start,
917                end,
918            });
919        }
920
921        let field_count = row_ranges.len();
922
923        let error = if !self.flexible {
924            error.or_else(|| match self.num_fields {
925                Some(expected) if field_count != expected => Some(ReadError::new(
926                    ReadErrorKind::InconsistentFieldCount {
927                        expected,
928                        found: field_count,
929                    },
930                    self.record_line,
931                    0,
932                )),
933                _ => None,
934            })
935        } else {
936            error
937        };
938
939        // Only a well-formed record may establish the expected field count.
940        // Otherwise one malformed record would cascade errors over the rest of
941        // the input.
942        if self.num_fields.is_none() && field_count > 0 && error.is_none() {
943            self.num_fields = Some(field_count);
944        }
945
946        BytesRow {
947            buf: row_buf,
948            ranges: row_ranges,
949            error,
950            line: self.record_line,
951        }
952    }
953}
954
955/// Counts line breaks in a slice of quoted-field content.
956///
957/// A `\r` immediately followed by `\n` counts as a single break, even when the
958/// pair is split across two slices: `pending_cr` is set when a slice ends on a
959/// bare `\r` and consulted by the following call.
960///
961/// Quoted content usually contains no line breaks at all, so a `memchr2`
962/// pre-scan (which is vectorised) keeps this off the hot path.
963fn count_line_breaks(bytes: &[u8], pending_cr: &mut bool) -> usize {
964    if memchr::memchr2(b'\r', b'\n', bytes).is_none() {
965        // No break in this slice, so a pending `\r` cannot pair with a leading
966        // `\n` here; any consumed byte clears it.
967        if !bytes.is_empty() {
968            *pending_cr = false;
969        }
970        return 0;
971    }
972
973    let mut breaks = 0;
974    let mut i = 0;
975    if *pending_cr {
976        if bytes.first() == Some(&b'\n') {
977            i = 1;
978        }
979        *pending_cr = false;
980    }
981    while i < bytes.len() {
982        match bytes[i] {
983            b'\n' => {
984                breaks += 1;
985                i += 1;
986            }
987            b'\r' => {
988                breaks += 1;
989                i += 1;
990                if i < bytes.len() {
991                    if bytes[i] == b'\n' {
992                        i += 1;
993                    }
994                } else {
995                    *pending_cr = true;
996                }
997            }
998            _ => i += 1,
999        }
1000    }
1001    breaks
1002}
1003
1004fn is_newline(b: u8) -> bool {
1005    b == b'\n' || b == b'\r'
1006}
1007
1008// ── BytesRow ──────────────────────────────────────────────────────────
1009
1010pub struct BytesRow {
1011    pub(crate) buf: Vec<u8>,
1012    pub(crate) ranges: Vec<FieldRange>,
1013    pub(crate) error: Option<ReadError>,
1014    pub(crate) line: usize,
1015}
1016
1017impl BytesRow {
1018    /// The parse error for this row, if any.
1019    ///
1020    /// Unlike [`Row`], a `BytesRow` never fails: the error is stored here and
1021    /// the raw field bytes remain accessible through [`get`](Self::get) and
1022    /// [`iter`](Self::iter). [`to_vec`](Self::to_vec) is the accessor that
1023    /// surfaces the error.
1024    pub fn error(&self) -> Option<&ReadError> {
1025        self.error.as_ref()
1026    }
1027
1028    pub fn len(&self) -> usize {
1029        self.ranges.len()
1030    }
1031
1032    pub fn is_empty(&self) -> bool {
1033        self.ranges.is_empty()
1034    }
1035
1036    pub fn get(&self, index: usize) -> Option<&[u8]> {
1037        self.ranges.get(index).map(|r| &self.buf[r.start..r.end])
1038    }
1039
1040    pub fn to_vec(&self) -> Result<Vec<&[u8]>, ReadError> {
1041        if let Some(ref e) = self.error {
1042            return Err(e.clone());
1043        }
1044        Ok(self.ranges.iter().map(|r| &self.buf[r.start..r.end]).collect())
1045    }
1046
1047    pub fn iter(&self) -> BytesFields<'_> {
1048        BytesFields {
1049            buf: &self.buf,
1050            iter: self.ranges.iter(),
1051        }
1052    }
1053}
1054
1055impl fmt::Debug for BytesRow {
1056    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1057        match &self.error {
1058            Some(e) => write!(f, "BytesRow(Err({e}))"),
1059            None => f
1060                .debug_list()
1061                .entries(self.ranges.iter().map(|r| &self.buf[r.start..r.end]))
1062                .finish(),
1063        }
1064    }
1065}
1066
1067pub struct BytesFields<'a> {
1068    buf: &'a [u8],
1069    iter: core::slice::Iter<'a, FieldRange>,
1070}
1071
1072impl<'a> Iterator for BytesFields<'a> {
1073    type Item = &'a [u8];
1074    fn next(&mut self) -> Option<&'a [u8]> {
1075        let r = self.iter.next()?;
1076        Some(&self.buf[r.start..r.end])
1077    }
1078    fn size_hint(&self) -> (usize, Option<usize>) {
1079        self.iter.size_hint()
1080    }
1081}
1082
1083impl<'a> ExactSizeIterator for BytesFields<'a> {}
1084
1085// ── Row ───────────────────────────────────────────────────────────────
1086
1087pub struct Row {
1088    pub(crate) inner: BytesRow,
1089    #[cfg(feature = "serde")]
1090    pub(crate) header_map: Option<Arc<BTreeMap<String, usize>>>,
1091    #[cfg(feature = "serde")]
1092    pub(crate) duplicate_headers: bool,
1093}
1094
1095impl Row {
1096    /// The parse error for this row, if any.
1097    pub fn error(&self) -> Option<&ReadError> {
1098        self.inner.error()
1099    }
1100
1101    /// Number of fields parsed for this row.
1102    ///
1103    /// Fields are still counted when the row carries an error.
1104    pub fn len(&self) -> usize {
1105        self.inner.len()
1106    }
1107
1108    pub fn is_empty(&self) -> bool {
1109        self.inner.is_empty()
1110    }
1111
1112    /// Returns the field at `index`, or `None` if there is no such field.
1113    ///
1114    /// Consistent with [`to_vec`](Self::to_vec) and [`iter`](Self::iter): if
1115    /// the row carries a parse error, every in-range index yields `Err`. Only
1116    /// an out-of-range index yields `None`.
1117    pub fn get(&self, index: usize) -> Option<Result<&str, ReadError>> {
1118        self.inner.get(index).map(|bytes| {
1119            if let Some(ref e) = self.inner.error {
1120                return Err(e.clone());
1121            }
1122            core::str::from_utf8(bytes).map_err(|_| ReadError::new(ReadErrorKind::InvalidUtf8, self.inner.line, 0))
1123        })
1124    }
1125
1126    /// Returns all fields as `&str`, or the row's parse error.
1127    pub fn to_vec(&self) -> Result<Vec<&str>, ReadError> {
1128        if let Some(ref e) = self.inner.error {
1129            return Err(e.clone());
1130        }
1131        self.inner
1132            .ranges
1133            .iter()
1134            .map(|r| {
1135                core::str::from_utf8(&self.inner.buf[r.start..r.end])
1136                    .map_err(|_| ReadError::new(ReadErrorKind::InvalidUtf8, self.inner.line, 0))
1137            })
1138            .collect()
1139    }
1140
1141    /// Iterates over the fields, yielding the row's parse error for each field
1142    /// when the row is malformed.
1143    pub fn iter(&self) -> Fields<'_> {
1144        Fields {
1145            buf: &self.inner.buf,
1146            iter: self.inner.ranges.iter(),
1147            error: self.inner.error.as_ref().map(|e| &e.kind),
1148            line: self.inner.line,
1149        }
1150    }
1151}
1152
1153impl fmt::Debug for Row {
1154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1155        match &self.inner.error {
1156            Some(e) => write!(f, "Row(Err({e}))"),
1157            None => {
1158                f.debug_list()
1159                    .entries(
1160                        self.inner.ranges.iter().map(|r| {
1161                            core::str::from_utf8(&self.inner.buf[r.start..r.end]).unwrap_or("<invalid utf-8>")
1162                        }),
1163                    )
1164                    .finish()
1165            }
1166        }
1167    }
1168}
1169
1170pub struct Fields<'a> {
1171    buf: &'a [u8],
1172    iter: core::slice::Iter<'a, FieldRange>,
1173    error: Option<&'a ReadErrorKind>,
1174    line: usize,
1175}
1176
1177impl<'a> Iterator for Fields<'a> {
1178    type Item = Result<&'a str, ReadError>;
1179    fn next(&mut self) -> Option<Self::Item> {
1180        let r = self.iter.next()?;
1181        if let Some(kind) = self.error {
1182            return Some(Err(ReadError::new(kind.clone(), self.line, 0)));
1183        }
1184        Some(
1185            core::str::from_utf8(&self.buf[r.start..r.end])
1186                .map_err(|_| ReadError::new(ReadErrorKind::InvalidUtf8, self.line, 0)),
1187        )
1188    }
1189    fn size_hint(&self) -> (usize, Option<usize>) {
1190        self.iter.size_hint()
1191    }
1192}
1193
1194impl<'a> ExactSizeIterator for Fields<'a> {}
1195
1196// ── Iterators ─────────────────────────────────────────────────────────
1197
1198pub struct BytesRows<'r, R: Read> {
1199    reader: &'r mut Reader<R>,
1200}
1201
1202impl<'r, R: Read> Iterator for BytesRows<'r, R> {
1203    type Item = BytesRow;
1204    fn next(&mut self) -> Option<Self::Item> {
1205        self.reader.read_row().map(|r| r.inner)
1206    }
1207}
1208
1209pub struct Rows<'r, R: Read> {
1210    reader: &'r mut Reader<R>,
1211}
1212
1213impl<'r, R: Read> Iterator for Rows<'r, R> {
1214    type Item = Row;
1215    fn next(&mut self) -> Option<Self::Item> {
1216        self.reader.read_row()
1217    }
1218}