diff --git a/src/bytes/mod.rs b/src/bytes/mod.rs index 96732cd57..a347a99a7 100644 --- a/src/bytes/mod.rs +++ b/src/bytes/mod.rs @@ -142,7 +142,14 @@ where let t = self.tag.clone(); match i.compare_no_case(t) { - CompareResult::Ok => Ok((i.take_from(tag_len), OM::Output::bind(|| i.take(tag_len)))), + CompareResult::Ok => { + // Case folding can change a char's byte length (e.g. U+212A KELVIN SIGN folds + // to 'k'), so split on the input's element boundary, not the tag length. + let index = i + .slice_index(self.tag.iter_elements().count()) + .unwrap_or(tag_len); + Ok((i.take_from(index), OM::Output::bind(|| i.take(index)))) + } CompareResult::Incomplete => { if OM::Incomplete::is_streaming() { Err(Err::Incomplete(Needed::new(tag_len - i.input_len()))) diff --git a/tests/issues.rs b/tests/issues.rs index 9996b5ccf..602d18a2d 100644 --- a/tests/issues.rs +++ b/tests/issues.rs @@ -281,3 +281,16 @@ fn issue_1808_complete_string_parser_returns_wrong_slice() { Ok(("", "\n")) ); } + +#[test] +fn issue_1883_tag_no_case_unicode_char_boundary() { + use nom::bytes::complete::tag_no_case; + + // U+212A KELVIN SIGN (3 bytes) case-folds to ASCII 'k'; the split must land on a + // char boundary, and the returned slice is the input's own prefix. + let r: IResult<&str, &str> = tag_no_case("k")("\u{212A}xyz"); + assert_eq!(r, Ok(("xyz", "\u{212A}"))); + + let r: IResult<&str, &str> = tag_no_case("aktz")("a\u{212A}tz!"); + assert_eq!(r, Ok(("!", "a\u{212A}tz"))); +}