Coverage Report

Created: 2025-07-11 07:04

/src/unicode-normalization/fuzz/fuzz_targets/streaming.rs
Line
Count
Source
1
//! Test that the NFC iterator doesn't run needlessly further ahead of its
2
//! underlying iterator.
3
//!
4
//! The NFC iterator is wrapped around the NFD iterator, and it buffers
5
//! up combining characters so that it can sort them once it knows it has
6
//! seen the complete sequence. At that point, it should drain its own
7
//! buffer before consuming more characters from its inner iterator.
8
//! This fuzz target defines a custom iterator which records how many
9
//! times it's called so we can detect if NFC called it too many times.
10
11
#![no_main]
12
13
#[macro_use]
14
extern crate libfuzzer_sys;
15
16
use std::cell::RefCell;
17
use std::rc::Rc;
18
use std::str::Chars;
19
use unicode_normalization::{char::canonical_combining_class, UnicodeNormalization};
20
21
const MAX_NONSTARTERS: u32 = 30;
22
23
#[derive(Debug)]
24
struct Counter<'a> {
25
    iter: Chars<'a>,
26
    value: Rc<RefCell<u32>>,
27
}
28
29
impl<'a> Iterator for Counter<'a> {
30
    type Item = char;
31
32
10.2M
    fn next(&mut self) -> Option<char> {
33
10.2M
        let next = self.iter.next();
34
10.2M
        if let Some(c) = next {
35
10.2M
            if canonical_combining_class(c) != 0 {
36
2.33M
                *self.value.borrow_mut() += 1;
37
7.89M
            }
38
2.12k
        }
39
10.2M
        next
40
10.2M
    }
41
}
42
43
fuzz_target!(|input: String| {
44
    let stream_safe = input.chars().stream_safe().collect::<String>();
45
46
    let value = Rc::new(RefCell::new(0));
47
    let counter = Counter {
48
        iter: stream_safe.chars(),
49
        value: Rc::clone(&value),
50
    };
51
    for _ in counter.nfc() {
52
        // Plus 1: The iterator may consume a starter that begins the next sequence.
53
        assert!(*value.borrow() <= MAX_NONSTARTERS + 1);
54
        *value.borrow_mut() = 0;
55
    }
56
});