Skip to content

Commit ede395f

Browse files
committed
Fix amazon-ion#955 by removing support for cr-only line endings
1 parent 0326292 commit ede395f

3 files changed

Lines changed: 16 additions & 137 deletions

File tree

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ sha2 = { version = "0.9", optional = true }
7171
serde = { version = "1.0", features = ["derive"], optional = true }
7272
serde_with = { version = "3.7.0", optional = true }
7373
visibility = "0.1.1"
74+
memchr = "2.7.4"
7475

7576
[dev-dependencies]
7677
rstest = "0.19.0"

‎src/lazy/value.rs‎

Lines changed: 13 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -595,107 +595,16 @@ mod tests {
595595
Ok(())
596596
}
597597

598-
#[rstest]
599-
#[case::no_crlf("{foo: 1, bar: 2}\"hello\"", (1,17))]
600-
#[case::cr_lf_lf("{foo: 1, bar: 2}\r\n\n\"hello\"", (3,1))]
601-
#[case::lf_lf_cr("{foo: 1, bar: 2}\n\n\r\"hello\"", (4,1))]
602-
#[case::cr_lf_cr("{foo: 1, bar: 2}\r\n\r\"hello\"", (3,1))]
603-
#[case::cr_cr_cr("{foo: 1, bar: 2}\r\r\r\"hello\"", (4,1))]
604-
#[case::cr_cr_lf("{foo: 1, bar: 2}\r\r\n\"hello\"", (3,1))]
605-
#[case::lf_cr_cr("{foo: 1, bar: 2}\n\r\r\"hello\"", (4,1))]
606-
#[case::lf_cr_lf("{foo: 1, bar: 2}\n\r\n\"hello\"", (3,1))]
607-
#[case::lf_lf_lf("{foo: 1, bar: 2}\n\n\n\"hello\"", (4,1))]
608-
#[case::newlines_after("{foo: 1, bar: 2}\"hello\"\n\n", (1, 17))]
609-
#[case::tabs("{foo: 1, bar: 2}\n\t\t\t\"hello\"", (2,4))]
610-
#[case::tabs_after("{foo: 1, bar: 2}\"hello\"\t\t", (1,17))]
611-
#[case::mix_tabs_and_newlines("{foo: 1, bar: 2}\n\t\n\"hello\"", (3,1))]
612-
#[case::long_string("{foo: 1, bar: 2}\n\n'''long \n\r\n\t hello'''", (3, 1))]
613-
#[case::comment("{foo: 1, bar: 2}\n\n /*multiline \n comment*/'''long \n\r\n\t hello'''", (4, 11))]
614-
#[case::on_same_line_as_preceding_multiline_value("{\n foo: 1,\n bar: 2\n}\"hello\"", (4, 2))]
615-
fn location_test_for_second_tlv(
616-
#[case] ion_text: &str,
617-
#[case] expected_location: (usize, usize),
618-
) -> IonResult<()> {
619-
let mut reader = Reader::new(v1_0::Text, ion_text)?;
620-
let result1 = reader.expect_next();
621-
622-
let expected_source_location =
623-
SourceLocation::new(expected_location.0, expected_location.1);
624-
assert!(result1.is_ok());
625-
if let Ok(lazy_value1) = result1 {
626-
let _val = lazy_value1.read();
627-
// first tlv will always be (1,1) per the examples here
628-
assert_eq!(lazy_value1.location(), SourceLocation::new(1, 1));
629-
}
630-
let result2 = reader.expect_next();
631-
assert!(result2.is_ok());
632-
if let Ok(lazy_value2) = result2 {
633-
let _val = lazy_value2.read();
634-
assert_eq!(lazy_value2.location(), expected_source_location);
635-
}
636-
Ok(())
637-
}
638-
639-
#[rstest]
640-
#[case::no_crlf(vec!["{foo: 1, bar: 2}","\"hello\""], (1,17))]
641-
#[case::cr_lf_lf(vec!["{foo: 1, ", "bar: 2}\r\n\n\"hello\""], (3,1))]
642-
#[case::lf_lf_cr(vec!["{foo: 1, bar: 2}","\n\n\r\"hello\""], (4,1))]
643-
#[case::cr_lf_cr(vec!["{foo: 1, bar: 2}\r\n\r","\"hello\""], (3,1))]
644-
#[case::cr_cr_cr(vec!["{foo: 1, bar: 2}\r\r\r","\"hello\""], (4,1))]
645-
#[case::cr_cr_lf(vec!["{foo: 1, bar: 2}\r\r\n\"he","llo\""], (3,1))]
646-
#[case::lf_cr_cr(vec!["{foo: 1, bar: 2}\n\r\r\"hello\""], (4,1))]
647-
#[case::lf_cr_lf(vec!["{foo: 1, bar: 2}\n\r\n\"hello\""], (3,1))]
648-
#[case::lf_lf_lf(vec!["{foo: 1, bar: 2}\n\n\n\"hello\""], (4,1))]
649-
#[case::newlines_after(vec!["{foo: 1, bar: 2}\"hello\"\n\n"], (1, 17))]
650-
#[case::tabs(vec!["{foo: 1, bar: 2}\n\t\t\t\"hello\""], (2,4))]
651-
#[case::tabs_after(vec!["{foo: 1, bar: 2}\"hello\"","\t\t"], (1,17))]
652-
#[case::mix_tabs_and_newlines(vec!["{foo: 1, bar: 2}\n\t\n\"hello\""], (3,1))]
653-
#[case::long_string(vec!["{foo: 1, bar: 2}\n\n'''long \n\r\n\t hello'''"], (3, 1))]
654-
#[case::comment(vec!["{foo: 1, bar: 2}\n\n", "/*multiline \n comment*/","'''long \n\r\n\t hello'''"], (4, 11))]
655-
#[case::on_same_line_as_preceding_multiline_value(vec!["{\n foo: 1,\n bar: 2\n}\"hello\""], (4, 2))]
656-
fn location_test_for_second_tlv_in_stream(
657-
#[case] ion_text: Vec<&str>,
658-
#[case] expected_location: (usize, usize),
659-
) -> IonResult<()> {
660-
use crate::IonStream;
661-
use std::io;
662-
use std::io::{Cursor, Read};
663-
664-
let expected_source_location =
665-
SourceLocation::new(expected_location.0, expected_location.1);
666-
let input_chunks = ion_text.as_slice();
667-
// Wrapping each string in an `io::Chain`
668-
let mut input: Box<dyn Read> = Box::new(io::empty());
669-
for input_chunk in input_chunks {
670-
input = Box::new(input.chain(Cursor::new(input_chunk)));
671-
}
672-
let mut reader = Reader::new(v1_0::Text, IonStream::new(input))?;
673-
let result1 = reader.expect_next();
674-
assert!(result1.is_ok());
675-
if let Ok(lazy_value1) = result1 {
676-
let _val = lazy_value1.read();
677-
// first tlv will always be (1,1) per the examples here
678-
assert_eq!(lazy_value1.location(), SourceLocation::new(1, 1));
679-
}
680-
let result2 = reader.expect_next();
681-
assert!(result2.is_ok());
682-
if let Ok(lazy_value2) = result2 {
683-
let _val = lazy_value2.read();
684-
assert_eq!(lazy_value2.location(), expected_source_location);
685-
}
686-
Ok(())
687-
}
688-
689598
#[rstest]
690599
#[case::no_crlf( "{}\"hello\"", [(1, 1), (1, 3)])]
691600
#[case::cr_lf_lf("{}\r\n\n\"hello\"", [(1, 1), (3, 1)] )]
692-
#[case::lf_lf_cr("{}\n\n\r\"hello\"", [(1, 1), (4, 1)] )]
693-
#[case::cr_lf_cr("{}\r\n\r\"hello\"", [(1, 1), (3, 1)] )]
694-
#[case::cr_cr_cr("{}\r\r\r\"hello\"", [(1, 1), (4, 1)] )]
695-
#[case::cr_cr_lf("{}\r\r\n\"hello\"", [(1, 1), (3, 1)] )]
696-
#[case::lf_cr_cr("{}\n\r\r\"hello\"", [(1, 1), (4, 1)] )]
601+
#[case::lf_lf_cr("{}\n\n\r\"hello\"", [(1, 1), (3, 2)] )]
602+
#[case::lf_cr_cr("{}\n\r\r\"hello\"", [(1, 1), (2, 3)] )]
697603
#[case::lf_cr_lf("{}\n\r\n\"hello\"", [(1, 1), (3, 1)] )]
698-
#[case::lf_lf_lf("{}\n\n\n\"hello\"", [(1, 1), (4, 1)] )]
604+
#[case::cr_lf_lf("{}\r\n\n\"hello\"", [(1, 1), (3, 1)] )]
605+
#[case::cr_lf_cr("{}\r\n\r\"hello\"", [(1, 1), (2, 2)] )]
606+
#[case::cr_cr_lf("{}\r\r\n\"hello\"", [(1, 1), (2, 1)] )]
607+
#[case::cr_cr_cr("{}\r\r\r\"hello\"", [(1, 1), (1, 6)] )]
699608
#[case::nl_after("{}\"hello\"\n\n", [(1, 1), (1, 3)])]
700609
#[case::tabs( "{}\n\t\t\"hello\"", [(1, 1), (2, 3)] )]
701610
#[case::tabs_after("{}\"hello\"\t\t", [(1, 1), (1, 3)])]
@@ -785,14 +694,13 @@ mod tests {
785694
#[rstest]
786695
#[case::no_crlf( "{}\"hello\"", [(1, 1), (1, 3)])]
787696
#[case::lf_lf_lf("{}\n\n\n\"hello\"", [(1, 1), (4, 1)] )]
788-
#[case::lf_lf_cr("{}\n\n\r\"hello\"", [(1, 1), (4, 1)] )]
789-
#[case::lf_cr_cr("{}\n\r\r\"hello\"", [(1, 1), (4, 1)] )]
790-
// FIXME: Currently failing because of https://github.com/amazon-ion/ion-rust/issues/955
791-
// #[case::lf_cr_lf("{}\n\r\n\"hello\"", [(1, 1), (3, 1)] )]
792-
// #[case::cr_lf_lf("{}\r\n\n\"hello\"", [(1, 1), (3, 1)] )]
793-
// #[case::cr_lf_cr("{}\r\n\r\"hello\"", [(1, 1), (3, 1)] )]
794-
// #[case::cr_cr_lf("{}\r\r\n\"hello\"", [(1, 1), (3, 1)] )]
795-
#[case::cr_cr_cr("{}\r\r\r\"hello\"", [(1, 1), (4, 1)] )]
697+
#[case::lf_lf_cr("{}\n\n\r\"hello\"", [(1, 1), (3, 2)] )]
698+
#[case::lf_cr_cr("{}\n\r\r\"hello\"", [(1, 1), (2, 3)] )]
699+
#[case::lf_cr_lf("{}\n\r\n\"hello\"", [(1, 1), (3, 1)] )]
700+
#[case::cr_lf_lf("{}\r\n\n\"hello\"", [(1, 1), (3, 1)] )]
701+
#[case::cr_lf_cr("{}\r\n\r\"hello\"", [(1, 1), (2, 2)] )]
702+
#[case::cr_cr_lf("{}\r\r\n\"hello\"", [(1, 1), (2, 1)] )]
703+
#[case::cr_cr_cr("{}\r\r\r\"hello\"", [(1, 1), (1, 6)] )]
796704
#[case::nl_after("{}\"hello\"\n\n", [(1, 1), (1, 3)])]
797705
#[case::tabs( "{}\n\t\t\"hello\"", [(1, 1), (2, 3)] )]
798706
#[case::tabs_after("{}\"hello\"\t\t", [(1, 1), (1, 3)])]

‎src/location.rs‎

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -99,40 +99,10 @@ impl SourceLocationState {
9999
pub fn update_from_source<T: AsRef<[u8]>>(&mut self, stream_offset: usize, data: T) {
100100
let data = data.as_ref();
101101
if !data.is_empty() {
102-
// Calculate `rows` based on occurrence of newline bytes. If we encounter:
103-
// 1. b'\r' then increment row count by 1.
104-
// 2.a. If there was no b'\r' encountered before b'\n' then increment row count by 1.
105-
// 2.b. If there was no b'\r' encountered before b'\n' then don't increment as b'\r\n' should be counted as 1 based on windows line ending pattern.
106-
// Calculate `prev_newline_offset` based on the index/offset value of the last seen newline byte.
107-
// Adding 1 to the index because we want to include everything after the newline -
108-
// if newline is at index 5, we want to start counting from index 6 (5 + 1).
109-
let (_, prev_newline_offsets) = data.iter().enumerate().fold(
110-
(false, vec![]),
111-
|(follows_cr, mut offset), (i, b)| {
112-
match (b, follows_cr) {
113-
// When there's a '\r', add a row and update the offset as this newline offset value
114-
(b'\r', _) => {
115-
offset.push(stream_offset + i + 1);
116-
(true, offset)
117-
}
118-
// When there's a '\n' not after '\r', add a row and update the offset as this newline offset value
119-
(b'\n', false) => {
120-
offset.push(stream_offset + i + 1);
121-
(false, offset)
122-
}
123-
// When there's '\n' immediately following '\r', update the offset without adding a row
124-
(b'\n', true) => {
125-
offset.pop();
126-
offset.push(stream_offset + i + 1);
127-
(false, offset)
128-
}
129-
_ => (false, offset),
130-
}
131-
},
132-
);
102+
let newlines = memchr::memchr_iter(b'\n', data);
133103
self.row_start_offsets
134104
.borrow_mut()
135-
.extend(prev_newline_offsets);
105+
.extend(newlines.map(|it| it + stream_offset + 1));
136106
}
137107
}
138108

0 commit comments

Comments
 (0)