1use std::borrow::Cow;
31
32use rudb_common::{Error, Result};
33
34use crate::dialect::Dialect;
35
36pub fn record(
48 bytes: &[u8],
49 from: usize,
50 dialect: Dialect,
51 eof: bool,
52 out: &mut Vec<String>,
53) -> Result<Option<usize>> {
54 if from >= bytes.len() {
55 return Ok(None);
60 }
61 let quote = dialect.quote_byte();
62 let escape = dialect.escape_byte();
63 let mut at = from;
64 let mut count = 0;
65 let mut field = Vec::new();
66 loop {
67 field.clear();
68 if bytes.get(at) == Some("e) {
69 at += 1;
70 loop {
71 let Some(&byte) = bytes.get(at) else { return Ok(None) };
72 if byte == escape && bytes.get(at + 1) == Some("e) {
73 field.push(quote);
74 at += 2;
75 continue;
76 }
77 if byte == quote {
78 at += 1;
79 break;
80 }
81 field.push(byte);
82 at += 1;
83 }
84 match bytes.get(at) {
85 None if !eof => return Ok(None),
86 None => {}
87 Some(&byte) if byte == dialect.delimiter || byte == b'\n' || byte == b'\r' => {}
88 Some(&byte) => return Err(after_quote(byte)),
89 }
90 } else {
91 while let Some(&byte) = bytes.get(at) {
92 if byte == dialect.delimiter || byte == b'\n' || byte == b'\r' {
93 break;
94 }
95 field.push(byte);
96 at += 1;
97 }
98 if at >= bytes.len() && !eof {
99 return Ok(None);
100 }
101 }
102 place(out, count, &field);
103 count += 1;
104 match bytes.get(at) {
105 Some(&byte) if byte == dialect.delimiter => at += 1,
106 Some(b'\r') => {
107 at += 1;
108 if bytes.get(at) == Some(&b'\n') {
109 at += 1;
110 } else if at >= bytes.len() && !eof {
111 return Ok(None);
114 }
115 break;
116 }
117 Some(b'\n') => {
118 at += 1;
119 break;
120 }
121 Some(_) => unreachable!("a field stops at a delimiter, a line ending or the end"),
122 None => break,
123 }
124 }
125 out.truncate(count);
126 Ok(Some(at))
127}
128
129fn place(out: &mut Vec<String>, at: usize, field: &[u8]) {
136 let text = String::from_utf8_lossy(field);
137 match out.get_mut(at) {
138 Some(held) => {
139 held.clear();
140 held.push_str(&text);
141 }
142 None => out.push(text.into_owned()),
143 }
144}
145
146fn after_quote(byte: u8) -> Error {
149 Error::io(format!(
150 "a quoted value is followed by '{}' rather than by a delimiter or the end of the line",
151 byte as char
152 ))
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub struct Span {
162 pub start: usize,
164 pub end: usize,
166 pub escaped: bool,
168}
169
170impl Span {
171 #[must_use]
176 pub const fn is_empty(self) -> bool {
177 self.start == self.end
178 }
179
180 #[must_use]
182 pub fn raw(self, bytes: &[u8]) -> &[u8] {
183 &bytes[self.start..self.end]
184 }
185
186 #[must_use]
193 pub fn text(self, bytes: &[u8], dialect: Dialect) -> Cow<'_, str> {
194 let raw = self.raw(bytes);
195 if !self.escaped {
196 return String::from_utf8_lossy(raw);
197 }
198 Cow::Owned(String::from_utf8_lossy(&unescape(raw, dialect)).into_owned())
199 }
200}
201
202fn unescape(raw: &[u8], dialect: Dialect) -> Vec<u8> {
209 let quote = dialect.quote_byte();
210 let escape = dialect.escape_byte();
211 let mut out = Vec::with_capacity(raw.len());
212 let mut at = 0;
213 while let Some(&byte) = raw.get(at) {
214 if byte == escape && raw.get(at + 1) == Some("e) {
215 out.push(quote);
216 at += 2;
217 } else {
218 out.push(byte);
219 at += 1;
220 }
221 }
222 out
223}
224
225#[derive(Debug, Clone, Default)]
230pub struct Records {
231 spans: Vec<Span>,
232 ends: Vec<usize>,
233}
234
235impl Records {
236 pub fn clear(&mut self) {
238 self.spans.clear();
239 self.ends.clear();
240 }
241
242 #[must_use]
244 pub fn len(&self) -> usize {
245 self.ends.len()
246 }
247
248 #[must_use]
250 pub fn is_empty(&self) -> bool {
251 self.ends.is_empty()
252 }
253
254 #[must_use]
260 pub fn fields(&self, at: usize) -> &[Span] {
261 let start = if at == 0 { 0 } else { self.ends[at - 1] };
262 &self.spans[start..self.ends[at]]
263 }
264
265 #[must_use]
267 pub fn field(&self, row: usize, column: usize) -> Option<Span> {
268 let start = if row == 0 { 0 } else { self.ends[row - 1] };
269 let at = start + column;
270 if at < self.ends[row] { Some(self.spans[at]) } else { None }
271 }
272
273 pub fn shift(&mut self, by: usize) {
276 if by == 0 {
277 return;
278 }
279 for span in &mut self.spans {
280 span.start -= by;
281 span.end -= by;
282 }
283 }
284}
285
286pub fn records(
299 bytes: &[u8],
300 from: usize,
301 dialect: Dialect,
302 eof: bool,
303 limit: usize,
304 out: &mut Records,
305) -> Result<usize> {
306 let quote = dialect.quote_byte();
307 let delimiter = dialect.delimiter;
308 let structural = |byte: u8| byte == quote || byte == b'\n' || byte == b'\r';
309 if dialect.escape_byte() == quote && !structural(delimiter) {
310 blocks(bytes, from, dialect, eof, limit, out)
311 } else {
312 let mut at = from;
313 while out.len() < limit {
314 let Some(next) = spans(bytes, at, dialect, eof, &mut out.spans)? else { break };
315 out.ends.push(out.spans.len());
316 at = next;
317 }
318 Ok(at)
319 }
320}
321
322fn blocks(
336 bytes: &[u8],
337 from: usize,
338 dialect: Dialect,
339 eof: bool,
340 limit: usize,
341 out: &mut Records,
342) -> Result<usize> {
343 let delimiter = dialect.delimiter;
344 let quote = dialect.quote_byte();
345 let len = bytes.len();
346 let mut record = from;
347 loop {
348 let mut mark = out.spans.len();
351 let mut field = record;
352 let mut quoted = false;
353 let mut inside = 0u64;
354 let mut block = record;
355 'blocks: while block < len && out.len() < limit {
356 let end = len.min(block + 64);
357 let (quotes, ends) = masks(&bytes[block..end], delimiter, quote);
358 let prefix = prefix_xor(quotes) ^ inside;
359 inside = 0u64.wrapping_sub(prefix >> 63);
360 let mut structural = ends & !prefix;
361 while structural != 0 {
362 let bit = structural.trailing_zeros() as usize;
363 structural &= structural - 1;
364 let at = block + bit;
365 if at < field {
366 continue;
368 }
369 let low = field.saturating_sub(block);
370 if quoted || quotes & below(bit) & !below(low) != 0 {
371 let Some(escaped) = enclosed(&bytes[field..at], quote) else {
372 break 'blocks;
373 };
374 out.spans.push(Span { start: field + 1, end: at - 1, escaped });
375 quoted = false;
376 } else {
377 out.spans.push(Span { start: field, end: at, escaped: false });
378 }
379 let byte = bytes[at];
380 if byte == delimiter {
381 field = at + 1;
382 continue;
383 }
384 let mut next = at + 1;
385 if byte == b'\r' {
386 match bytes.get(next) {
387 Some(b'\n') => next += 1,
388 Some(_) => {}
389 None if eof => {}
390 None => {
393 out.spans.truncate(mark);
394 return Ok(record);
395 }
396 }
397 }
398 out.ends.push(out.spans.len());
399 mark = out.spans.len();
400 record = next;
401 field = next;
402 if out.len() == limit {
403 return Ok(record);
404 }
405 }
406 if field < end && quotes >> field.saturating_sub(block) != 0 {
407 quoted = true;
408 }
409 block = end;
410 }
411 if out.len() >= limit || record >= len {
412 return Ok(record);
413 }
414 out.spans.truncate(mark);
418 let Some(next) = spans(bytes, record, dialect, eof, &mut out.spans)? else {
419 return Ok(record);
420 };
421 out.ends.push(out.spans.len());
422 record = next;
423 }
424}
425
426#[inline]
434fn masks(block: &[u8], delimiter: u8, quote: u8) -> (u64, u64) {
435 let mut padded = [0u8; 64];
436 let full: &[u8; 64] = if let Ok(full) = block.try_into() {
437 full
438 } else {
439 padded[..block.len()].copy_from_slice(block);
440 &padded
441 };
442 let mut class = [0u8; 64];
446 for (class, &byte) in class.iter_mut().zip(full) {
447 let end = (byte == delimiter) | (byte == b'\n') | (byte == b'\r');
448 *class = u8::from(byte == quote) | (u8::from(end) << 1);
449 }
450 let mut quotes = 0u64;
451 let mut ends = 0u64;
452 for (at, eight) in class.chunks_exact(8).enumerate() {
453 let word = u64::from_le_bytes(eight.try_into().expect("eight bytes"));
454 quotes |= gather(word & LOW_BITS) << (8 * at);
455 ends |= gather((word >> 1) & LOW_BITS) << (8 * at);
456 }
457 if block.len() < 64 {
458 let live = below(block.len());
459 quotes &= live;
460 ends &= live;
461 }
462 (quotes, ends)
463}
464
465const LOW_BITS: u64 = 0x0101_0101_0101_0101;
467
468const fn gather(word: u64) -> u64 {
473 word.wrapping_mul(0x0102_0408_1020_4080) >> 56
474}
475
476const fn prefix_xor(mut bits: u64) -> u64 {
478 bits ^= bits << 1;
479 bits ^= bits << 2;
480 bits ^= bits << 4;
481 bits ^= bits << 8;
482 bits ^= bits << 16;
483 bits ^= bits << 32;
484 bits
485}
486
487const fn below(count: usize) -> u64 {
489 (1u64 << count) - 1
490}
491
492fn enclosed(field: &[u8], quote: u8) -> Option<bool> {
499 let [first, inner @ .., last] = field else { return None };
500 if *first != quote || *last != quote {
501 return None;
502 }
503 let mut escaped = false;
504 let mut at = 0;
505 while let Some(found) = inner[at..].iter().position(|&byte| byte == quote) {
506 if inner.get(at + found + 1) != Some("e) {
507 return None;
508 }
509 escaped = true;
510 at += found + 2;
511 }
512 Some(escaped)
513}
514
515fn spans(
520 bytes: &[u8],
521 from: usize,
522 dialect: Dialect,
523 eof: bool,
524 out: &mut Vec<Span>,
525) -> Result<Option<usize>> {
526 if from >= bytes.len() {
527 return Ok(None);
528 }
529 let mark = out.len();
530 let quote = dialect.quote_byte();
531 let escape = dialect.escape_byte();
532 let mut at = from;
533 loop {
534 let span;
535 if bytes.get(at) == Some("e) {
536 at += 1;
537 let start = at;
538 let mut escaped = false;
539 loop {
540 let Some(&byte) = bytes.get(at) else {
541 out.truncate(mark);
542 return Ok(None);
543 };
544 if byte == escape && bytes.get(at + 1) == Some("e) {
545 escaped = true;
546 at += 2;
547 continue;
548 }
549 if byte == quote {
550 break;
551 }
552 at += 1;
553 }
554 span = Span { start, end: at, escaped };
555 at += 1;
556 match bytes.get(at) {
557 None if !eof => {
558 out.truncate(mark);
559 return Ok(None);
560 }
561 None => {}
562 Some(&byte) if byte == dialect.delimiter || byte == b'\n' || byte == b'\r' => {}
563 Some(&byte) => {
564 out.truncate(mark);
565 return Err(after_quote(byte));
566 }
567 }
568 } else {
569 let start = at;
570 while let Some(&byte) = bytes.get(at) {
571 if byte == dialect.delimiter || byte == b'\n' || byte == b'\r' {
572 break;
573 }
574 at += 1;
575 }
576 if at >= bytes.len() && !eof {
577 out.truncate(mark);
578 return Ok(None);
579 }
580 span = Span { start, end: at, escaped: false };
581 }
582 out.push(span);
583 match bytes.get(at) {
584 Some(&byte) if byte == dialect.delimiter => at += 1,
585 Some(b'\r') => {
586 at += 1;
587 if bytes.get(at) == Some(&b'\n') {
588 at += 1;
589 } else if at >= bytes.len() && !eof {
590 out.truncate(mark);
591 return Ok(None);
592 }
593 break;
594 }
595 Some(b'\n') => {
596 at += 1;
597 break;
598 }
599 Some(_) => unreachable!("a field stops at a delimiter, a line ending or the end"),
600 None => break,
601 }
602 }
603 Ok(Some(at))
604}
605
606#[cfg(test)]
607mod tests {
608 use super::*;
609
610 fn split(bytes: &[u8], dialect: Dialect) -> Vec<Vec<String>> {
611 let mut rows = Vec::new();
612 let mut fields = Vec::new();
613 let mut at = 0;
614 while at < bytes.len() {
615 let next = record(bytes, at, dialect, true, &mut fields)
616 .expect("splits")
617 .expect("a whole record");
618 rows.push(fields.clone());
619 at = next;
620 }
621 rows
622 }
623
624 fn comma() -> Dialect {
625 Dialect { delimiter: b',', quote: Some(b'"'), escape: Some(b'"'), header: true }
626 }
627
628 #[test]
629 fn a_line_of_fields_is_the_fields_of_that_line() {
630 assert_eq!(split(b"a,b,c\n1,2,3\n", comma()), [["a", "b", "c"], ["1", "2", "3"]]);
631 }
632
633 #[test]
634 fn the_last_line_does_not_need_a_newline_on_it() {
635 assert_eq!(split(b"a,b\n1,2", comma()), [["a", "b"], ["1", "2"]]);
636 }
637
638 #[test]
639 fn all_three_line_endings_end_a_line() {
640 assert_eq!(split(b"a\r\nb\rc\n", comma()), [["a"], ["b"], ["c"]]);
641 }
642
643 #[test]
644 fn a_quoted_field_may_hold_the_delimiter_and_a_newline() {
645 assert_eq!(split(b"1,\"x,y\"\n", comma()), [["1", "x,y"]]);
646 assert_eq!(split(b"1,\"x\ny\"\n", comma()), [["1", "x\ny"]]);
647 }
648
649 #[test]
650 fn a_doubled_quote_inside_a_quoted_field_is_one_quote() {
651 assert_eq!(split(b"1,\"say \"\"hi\"\"\"\n", comma()), [["1", "say \"hi\""]]);
652 }
653
654 #[test]
655 fn an_empty_field_is_an_empty_string_here_and_becomes_a_null_above() {
656 assert_eq!(split(b"1,,3\n", comma()), [["1", "", "3"]]);
657 assert_eq!(split(b"1,\"\",3\n", comma()), [["1", "", "3"]]);
658 }
659
660 #[test]
661 fn a_trailing_delimiter_makes_a_last_empty_field() {
662 assert_eq!(split(b"1|x|\n", Dialect { delimiter: b'|', ..comma() }), [["1", "x", ""]]);
663 }
664
665 #[test]
666 fn a_quote_in_the_middle_of_a_bare_field_is_just_a_character() {
667 assert_eq!(split(b"1,he said \"hi\"\n", comma()), [["1", "he said \"hi\""]]);
668 }
669
670 #[test]
671 fn a_record_that_the_buffer_does_not_hold_all_of_is_not_a_record_yet() {
672 let mut fields = Vec::new();
673 assert_eq!(record(b"a,b", 0, comma(), false, &mut fields).unwrap(), None);
674 assert_eq!(record(b"a,\"b", 0, comma(), true, &mut fields).unwrap(), None);
675 assert_eq!(record(b"a,b\n", 0, comma(), false, &mut fields).unwrap(), Some(4));
676 }
677
678 #[test]
679 fn rubbish_after_a_closing_quote_is_an_error_rather_than_a_guess() {
680 let mut fields = Vec::new();
681 let error = record(b"\"x\"y,2\n", 0, comma(), true, &mut fields).unwrap_err();
682 assert!(error.message().contains("quoted value"), "{error}");
683 }
684
685 #[test]
686 fn utf8_survives_being_read_one_byte_at_a_time() {
687 assert_eq!(split("a,héllo\n".as_bytes(), comma()), [["a", "héllo"]]);
688 }
689}
690
691#[cfg(test)]
697mod agree {
698 use super::*;
699
700 type Outcome = (Vec<Vec<String>>, usize, Option<String>);
702
703 fn by_record(bytes: &[u8], from: usize, dialect: Dialect, eof: bool) -> Outcome {
704 let mut rows = Vec::new();
705 let mut fields = Vec::new();
706 let mut at = from;
707 loop {
708 match record(bytes, at, dialect, eof, &mut fields) {
709 Ok(Some(next)) => {
710 rows.push(fields.clone());
711 at = next;
712 }
713 Ok(None) => return (rows, at, None),
714 Err(error) => return (rows, at, Some(error.to_string())),
715 }
716 }
717 }
718
719 fn by_chunk(bytes: &[u8], from: usize, dialect: Dialect, eof: bool, limit: usize) -> Outcome {
720 let mut rows = Vec::new();
721 let mut at = from;
722 let mut out = Records::default();
723 loop {
724 out.clear();
725 let result = records(bytes, at, dialect, eof, limit, &mut out);
726 for row in 0..out.len() {
727 rows.push(
728 out.fields(row)
729 .iter()
730 .map(|span| span.text(bytes, dialect).into_owned())
731 .collect(),
732 );
733 }
734 match result {
735 Ok(next) => {
736 at = next;
737 if out.len() < limit {
738 return (rows, at, None);
739 }
740 }
741 Err(error) => {
742 let mut from = at;
745 let mut fields = Vec::new();
746 for _ in 0..out.len() {
747 from = record(bytes, from, dialect, eof, &mut fields).unwrap().unwrap();
748 }
749 return (rows, from, Some(error.to_string()));
750 }
751 }
752 }
753 }
754
755 fn check(bytes: &[u8], dialect: Dialect, cuts: &[usize]) {
758 for eof in [true, false] {
759 let expected = by_record(bytes, 0, dialect, eof);
760 for limit in [1, 2, 3, 7, 8192] {
761 assert_eq!(
762 by_chunk(bytes, 0, dialect, eof, limit),
763 expected,
764 "{:?} under {dialect:?}, eof {eof}, {limit} at a time",
765 String::from_utf8_lossy(bytes),
766 );
767 }
768 }
769 let whole = by_record(bytes, 0, dialect, true);
770 for &cut in cuts {
771 let cut = cut.min(bytes.len());
772 let (mut rows, at, error) = by_chunk(&bytes[..cut], 0, dialect, false, 8192);
775 if error.is_some() {
776 assert_eq!(error, whole.2, "an error in the front is the error in the whole");
777 continue;
778 }
779 let (rest, end, error) = by_chunk(bytes, at, dialect, true, 8192);
780 rows.extend(rest);
781 assert_eq!(
782 (rows, end, error),
783 whole,
784 "{:?} under {dialect:?} cut at {cut}",
785 String::from_utf8_lossy(bytes),
786 );
787 }
788 }
789
790 fn dialects() -> [Dialect; 5] {
791 let comma =
792 Dialect { delimiter: b',', quote: Some(b'"'), escape: Some(b'"'), header: false };
793 [
794 comma,
795 Dialect { quote: None, escape: None, ..comma },
796 Dialect { delimiter: b'|', ..comma },
797 Dialect { delimiter: b'\t', quote: Some(b'\''), escape: Some(b'\''), ..comma },
798 Dialect { escape: Some(b'\\'), ..comma },
799 ]
800 }
801
802 #[test]
803 fn the_tricky_ones_split_the_same_both_ways() {
804 let long = "x".repeat(61);
805 let cases: Vec<Vec<u8>> = vec![
806 b"".to_vec(),
807 b"\n".to_vec(),
808 b"\n\n".to_vec(),
809 b"a".to_vec(),
810 b"a,b,c\n1,2,3\n".to_vec(),
811 b"a,b\n1,2".to_vec(),
812 b"a\r\nb\rc\n".to_vec(),
813 b"a\r".to_vec(),
814 b"a\r\r\n\n".to_vec(),
815 b"1,\"x,y\"\n2,\"x\ny\"\n".to_vec(),
816 b"1,\"say \"\"hi\"\"\"\n".to_vec(),
817 b"1,,3\n1,\"\",3\n".to_vec(),
818 b"1|x|\n1,x,\n".to_vec(),
819 b"1,he said \"hi\"\n2,x\n".to_vec(),
820 b"1,he said \"hi\n2,x\n".to_vec(),
821 b"\"x\"y,2\n".to_vec(),
822 b"\"x\"\"\n".to_vec(),
823 b"\"x\"\"".to_vec(),
824 b"a,\"b".to_vec(),
825 b"a,\"b\"".to_vec(),
826 b"\"".to_vec(),
827 b"\"\"\"\"\n".to_vec(),
828 b"\"a\"\"\",b\n".to_vec(),
829 b"\"a\\\"b\",c\n\"a\\\\\"\n".to_vec(),
830 b"'a,b'\t'c''d'\n".to_vec(),
831 b"x\xffy,\"\xfe\"\"\"\n".to_vec(),
832 "h\u{e9}llo,w\u{f6}rld\n".as_bytes().to_vec(),
833 format!("{long},\"a\nb\",c\n{long}\r\n\"{long}\"\"{long}\",d\n").into_bytes(),
834 format!("{long}ab\r\n{long}abc\r\n").into_bytes(),
835 format!("\"{long}\"\"\",\"\n\"\n{long},x\"\n").into_bytes(),
836 ];
837 for bytes in &cases {
838 let cuts: Vec<usize> = (0..=bytes.len()).collect();
839 for dialect in dialects() {
840 check(bytes, dialect, &cuts);
841 }
842 }
843 }
844
845 struct Rng(u64);
847
848 impl Rng {
849 fn next(&mut self) -> u64 {
850 self.0 ^= self.0 << 13;
851 self.0 ^= self.0 >> 7;
852 self.0 ^= self.0 << 17;
853 self.0
854 }
855
856 fn below(&mut self, n: usize) -> usize {
857 (self.next() % n as u64) as usize
858 }
859 }
860
861 fn generate(rng: &mut Rng) -> Vec<u8> {
863 const PIECES: [&[u8]; 20] = [
864 b"a",
865 b"1",
866 b"-2.5",
867 b"xyz",
868 b",",
869 b"|",
870 b"\t",
871 b"\"",
872 b"\"\"",
873 b"'",
874 b"\\",
875 b"\n",
876 b"\r",
877 b"\r\n",
878 b" ",
879 b"\xc3\xa9",
880 b"\xff",
881 b"2020-01-02",
882 b"",
883 b"0123456789abcdef",
884 ];
885 let mut out = Vec::new();
886 let rows = rng.below(40);
887 for _ in 0..rows {
888 let fields = 1 + rng.below(5);
889 for field in 0..fields {
890 if field > 0 {
891 out.push(b",,,|\t"[rng.below(5)]);
892 }
893 let mut body = Vec::new();
894 for _ in 0..rng.below(6) {
895 let piece = PIECES[rng.below(PIECES.len())];
896 if rng.below(10) == 0 {
898 body.extend(std::iter::repeat_n(b'q', rng.below(90)));
899 }
900 body.extend_from_slice(piece);
901 }
902 match rng.below(4) {
903 0 => {
904 out.push(b'"');
905 for &byte in &body {
906 if byte == b'"' {
907 out.push(b'"');
908 }
909 out.push(byte);
910 }
911 out.push(b'"');
912 }
913 1 => {
914 out.push(b'"');
915 out.extend_from_slice(&body);
916 out.push(b'"');
917 }
918 _ => out.extend_from_slice(&body),
919 }
920 }
921 out.extend_from_slice([&b"\n"[..], b"\r\n", b"\r"][rng.below(3)]);
922 }
923 if rng.below(3) == 0 {
924 out.truncate(out.len().saturating_sub(1 + rng.below(3)));
925 }
926 out
927 }
928
929 #[test]
930 fn thousands_of_generated_files_split_the_same_both_ways() {
931 let mut rng = Rng(0x9e37_79b9_7f4a_7c15);
932 for _ in 0..3000 {
933 let bytes = generate(&mut rng);
934 let cuts: Vec<usize> = (0..4).map(|_| rng.below(bytes.len() + 1)).collect();
935 for dialect in dialects() {
936 check(&bytes, dialect, &cuts);
937 }
938 }
939 }
940}