IgottheideaandgeneralimplementationstrategyfromRussCoxinhis [articleonregexps](https://web.archive.org/web/20160404141123/https://swtch.com/~rsc/regexp/regexp3.html) and RE2. RussCoxgotitfromKenThompson's`grep`(nosource,folklore?). Ialsogottheideafrom [Lucene](https://github.com/apache/lucene-solr/blob/ae93f4e7ac6a3908046391de35d4f50a0d3c59ca/lucene/core/src/java/org/apache/lucene/util/automaton/UTF32ToUTF8.java), whichusesitforexecutingautomataontheirtermindex.
*/
use core::{char, fmt, iter::FusedIterator, slice};
use alloc::{vec, vec::Vec};
const MAX_UTF8_BYTES: usize = 4;
/// Utf8Sequence represents a sequence of byte ranges. /// /// To match a Utf8Sequence, a candidate byte sequence must match each /// successive range. /// /// For example, if there are two ranges, `[C2-DF][80-BF]`, then the byte /// sequence `\xDD\x61` would not match because `0x61 < 0x80`. #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] pubenum Utf8Sequence { /// One byte range.
One(Utf8Range), /// Two successive byte ranges.
Two([Utf8Range; 2]), /// Three successive byte ranges.
Three([Utf8Range; 3]), /// Four successive byte ranges.
Four([Utf8Range; 4]),
}
impl Utf8Sequence { /// Creates a new UTF-8 sequence from the encoded bytes of a scalar value /// range. /// /// This assumes that `start` and `end` have the same length. fn from_encoded_range(start: &[u8], end: &[u8]) -> Self {
assert_eq!(start.len(), end.len()); match start.len() { 2 => Utf8Sequence::Two([
Utf8Range::new(start[0], end[0]),
Utf8Range::new(start[1], end[1]),
]), 3 => Utf8Sequence::Three([
Utf8Range::new(start[0], end[0]),
Utf8Range::new(start[1], end[1]),
Utf8Range::new(start[2], end[2]),
]), 4 => Utf8Sequence::Four([
Utf8Range::new(start[0], end[0]),
Utf8Range::new(start[1], end[1]),
Utf8Range::new(start[2], end[2]),
Utf8Range::new(start[3], end[3]),
]),
n => unreachable!("invalid encoded length: {}", n),
}
}
/// Returns the underlying sequence of byte ranges as a slice. pubfn as_slice(&self) -> &[Utf8Range] { useself::Utf8Sequence::*; match *self {
One(ref r) => slice::from_ref(r),
Two(ref r) => &r[..],
Three(ref r) => &r[..],
Four(ref r) => &r[..],
}
}
/// Returns the number of byte ranges in this sequence. /// /// The length is guaranteed to be in the closed interval `[1, 4]`. pubfn len(&self) -> usize { self.as_slice().len()
}
/// Reverses the ranges in this sequence. /// /// For example, if this corresponds to the following sequence: /// /// ```text /// [D0-D3][80-BF] /// ``` /// /// Then after reversal, it will be /// /// ```text /// [80-BF][D0-D3] /// ``` /// /// This is useful when one is constructing a UTF-8 automaton to match /// character classes in reverse. pubfn reverse(&mutself) { match *self {
Utf8Sequence::One(_) => {}
Utf8Sequence::Two(refmut x) => x.reverse(),
Utf8Sequence::Three(refmut x) => x.reverse(),
Utf8Sequence::Four(refmut x) => x.reverse(),
}
}
/// Returns true if and only if a prefix of `bytes` matches this sequence /// of byte ranges. pubfn matches(&self, bytes: &[u8]) -> bool { if bytes.len() < self.len() { returnfalse;
} for (&b, r) in bytes.iter().zip(self) { if !r.matches(b) { returnfalse;
}
} true
}
}
impl<'a> IntoIterator for &'a Utf8Sequence { type IntoIter = slice::Iter<'a, Utf8Range>; type Item = &'a Utf8Range;
/// A single inclusive range of UTF-8 bytes. #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)] pubstruct Utf8Range { /// Start of byte range (inclusive). pub start: u8, /// End of byte range (inclusive). pub end: u8,
}
/// An iterator over ranges of matching UTF-8 byte sequences. /// /// The iteration represents an alternation of comprehensive byte sequences /// that match precisely the set of UTF-8 encoded scalar values. /// /// A byte sequence corresponds to one of the scalar values in the range given /// if and only if it completely matches exactly one of the sequences of byte /// ranges produced by this iterator. /// /// Each sequence of byte ranges matches a unique set of bytes. That is, no two /// sequences will match the same bytes. /// /// # Example /// /// This shows how to match an arbitrary byte sequence against a range of /// scalar values. /// /// ```rust /// use regex_syntax::utf8::{Utf8Sequences, Utf8Sequence}; /// /// fn matches(seqs: &[Utf8Sequence], bytes: &[u8]) -> bool { /// for range in seqs { /// if range.matches(bytes) { /// return true; /// } /// } /// false /// } /// /// // Test the basic multilingual plane. /// let seqs: Vec<_> = Utf8Sequences::new('\u{0}', '\u{FFFF}').collect(); /// /// // UTF-8 encoding of 'a'. /// assert!(matches(&seqs, &[0x61])); /// // UTF-8 encoding of '☃' (`\u{2603}`). /// assert!(matches(&seqs, &[0xE2, 0x98, 0x83])); /// // UTF-8 encoding of `\u{10348}` (outside the BMP). /// assert!(!matches(&seqs, &[0xF0, 0x90, 0x8D, 0x88])); /// // Tries to match against a UTF-8 encoding of a surrogate codepoint, /// // which is invalid UTF-8, and therefore fails, despite the fact that /// // the corresponding codepoint (0xD800) falls in the range given. /// assert!(!matches(&seqs, &[0xED, 0xA0, 0x80])); /// // And fails against plain old invalid UTF-8. /// assert!(!matches(&seqs, &[0xFF, 0xFF])); /// ``` /// /// If this example seems circuitous, that's because it is! It's meant to be /// illustrative. In practice, you could just try to decode your byte sequence /// and compare it with the scalar value range directly. However, this is not /// always possible (for example, in a byte based automaton). #[derive(Debug)] pubstruct Utf8Sequences {
range_stack: Vec<ScalarRange>,
}
impl Utf8Sequences { /// Create a new iterator over UTF-8 byte ranges for the scalar value range /// given. pubfn new(start: char, end: char) -> Self { letmut it = Utf8Sequences { range_stack: vec![] };
it.push(u32::from(start), u32::from(end));
it
}
/// reset resets the scalar value range. /// Any existing state is cleared, but resources may be reused. /// /// N.B. Benchmarks say that this method is dubious. #[doc(hidden)] pubfn reset(&mutself, start: char, end: char) { self.range_stack.clear(); self.push(u32::from(start), u32::from(end));
}
impl Iterator for Utf8Sequences { type Item = Utf8Sequence;
fn next(&mutself) -> Option<Self::Item> { 'TOP: while let Some(mut r) = self.range_stack.pop() { 'INNER: loop { iflet Some((r1, r2)) = r.split() { self.push(r2.start, r2.end);
r.start = r1.start;
r.end = r1.end; continue'INNER;
} if !r.is_valid() { continue'TOP;
} for i in1..MAX_UTF8_BYTES { let max = max_scalar_value(i); if r.start <= max && max < r.end { self.push(max + 1, r.end);
r.end = max; continue'INNER;
}
} iflet Some(ascii_range) = r.as_ascii() { return Some(Utf8Sequence::One(ascii_range));
} for i in1..MAX_UTF8_BYTES { let m = (1 << (6 * i)) - 1; if (r.start & !m) != (r.end & !m) { if (r.start & m) != 0 { self.push((r.start | m) + 1, r.end);
r.end = r.start | m; continue'INNER;
} if (r.end & m) != m { self.push(r.end & !m, r.end);
r.end = (r.end & !m) - 1; continue'INNER;
}
}
} letmut start = [0; MAX_UTF8_BYTES]; letmut end = [0; MAX_UTF8_BYTES]; let n = r.encode(&mut start, &mut end); return Some(Utf8Sequence::from_encoded_range(
&start[0..n],
&end[0..n],
));
}
}
None
}
}
impl FusedIterator for Utf8Sequences {}
impl ScalarRange { /// split splits this range if it overlaps with a surrogate codepoint. /// /// Either or both ranges may be invalid. fn split(&self) -> Option<(ScalarRange, ScalarRange)> { ifself.start < 0xE000 && self.end > 0xD7FF {
Some((
ScalarRange { start: self.start, end: 0xD7FF },
ScalarRange { start: 0xE000, end: self.end },
))
} else {
None
}
}
/// is_valid returns true if and only if start <= end. fn is_valid(&self) -> bool { self.start <= self.end
}
/// as_ascii returns this range as a Utf8Range if and only if all scalar /// values in this range can be encoded as a single byte. fn as_ascii(&self) -> Option<Utf8Range> { ifself.is_ascii() { let start = u8::try_from(self.start).unwrap(); let end = u8::try_from(self.end).unwrap();
Some(Utf8Range::new(start, end))
} else {
None
}
}
/// is_ascii returns true if the range is ASCII only (i.e., takes a single /// byte to encode any scalar value). fn is_ascii(&self) -> bool { self.is_valid() && self.end <= 0x7f
}
/// encode writes the UTF-8 encoding of the start and end of this range /// to the corresponding destination slices, and returns the number of /// bytes written. /// /// The slices should have room for at least `MAX_UTF8_BYTES`. fn encode(&self, start: &mut [u8], end: &an style='color:red'>mut [u8]) -> usize { let cs = char::from_u32(self.start).unwrap(); let ce = char::from_u32(self.end).unwrap(); let ss = cs.encode_utf8(start); let se = ce.encode_utf8(end);
assert_eq!(ss.len(), se.len());
ss.len()
}
}
#[test] fn single_codepoint_one_sequence() { // Tests that every range of scalar values that contains a single // scalar value is recognized by one sequence of byte ranges. for i in0x0..=0x0010_FFFF { let c = match char::from_u32(i) {
None => continue,
Some(c) => c,
}; let seqs: Vec<_> = Utf8Sequences::new(c, c).collect();
assert_eq!(seqs.len(), 1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.