unicode-intervals 0.3.0

Search for Unicode code points intervals by including/excluding categories, ranges, and custom characters sets.
Documentation
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use unicode_intervals::{internals, UnicodeCategory, UnicodeCategorySet, UnicodeVersion};

fn version(c: &mut Criterion) {
    let version = black_box(UnicodeVersion::V15_0_0);
    c.bench_function("version - normalized_categories", |b| {
        b.iter(|| {
            let _ = version.normalized_categories();
        })
    });
}

fn intervals(c: &mut Criterion) {
    let string = black_box("abcdef0123456789");
    let long_string =
        black_box("zxcvbnm,./asdfghjkl;'qwertyuiopZXCVBNM<>?ASDFGHJKL:QWERTYUIOP{}0123456");
    c.bench_function("intervals - from_str short", |b| {
        b.iter(|| internals::intervals::from_str(string))
    });
    c.bench_function("intervals - from_str long", |b| {
        b.iter(|| internals::intervals::from_str(long_string))
    });
    let uppercase = UnicodeVersion::V15_0_0.intervals_for(UnicodeCategory::Lu);
    let lowercase = UnicodeVersion::V15_0_0.intervals_for(UnicodeCategory::Ll);
    c.bench_function("intervals - subtract", |b| {
        b.iter(|| internals::intervals::subtract(lowercase.to_vec(), uppercase))
    });
    // Realistic `query` shape: a large category minus a few scattered codepoints
    // (e.g. `exclude_characters`), so most of `left` is copied in bulk runs.
    let large = UnicodeVersion::V15_0_0.intervals_for(UnicodeCategory::Lo);
    let few = internals::intervals::from_str("\u{0E01}\u{4E2D}\u{A000}");
    c.bench_function("intervals - subtract sparse", |b| {
        b.iter(|| internals::intervals::subtract(large.to_vec(), &few))
    });
}

fn categories(c: &mut Criterion) {
    let all_categories = black_box(UnicodeCategorySet::all());
    c.bench_function("categories - set - display - all", |b| {
        b.iter(|| {
            let _ = all_categories.to_string();
        })
    });
    let few_categories = UnicodeCategory::Lm | UnicodeCategory::Sk | UnicodeCategory::Zl;
    c.bench_function("categories - set - display - few", |b| {
        b.iter(|| {
            let _ = few_categories.to_string();
        })
    });
    c.bench_function("categories - merge", |b| {
        b.iter(|| {
            let _ = internals::categories::merge(
                Some(all_categories),
                black_box(UnicodeCategory::Lu.into()),
            );
        })
    });
}

fn query(c: &mut Criterion) {
    let version = black_box(UnicodeVersion::V15_0_0);
    c.bench_function("query - intervals_for_set - empty", |b| {
        b.iter(|| {
            internals::query::intervals_for_set(version, black_box(UnicodeCategorySet::new()))
        })
    });
    c.bench_function("query - intervals_for_set - all", |b| {
        b.iter(|| {
            internals::query::intervals_for_set(version, black_box(UnicodeCategorySet::all()))
        })
    });
    c.bench_function("query - intervals_for_set - single large", |b| {
        b.iter(|| {
            internals::query::intervals_for_set(version, black_box(UnicodeCategory::Lu).into())
        })
    });
    c.bench_function("query - intervals_for_set - multiple", |b| {
        b.iter(|| {
            internals::query::intervals_for_set(
                version,
                black_box(UnicodeCategory::Lu | UnicodeCategory::M),
            )
        })
    });
    let exclude_categories = black_box(UnicodeCategory::Lu);
    let min_codepoint = black_box(0);
    let max_codepoint = black_box(128);
    c.bench_function("query - top level - only codepoints", |b| {
        b.iter(|| {
            let _ = version
                .query()
                .exclude_categories(exclude_categories)
                .min_codepoint(min_codepoint)
                .max_codepoint(max_codepoint)
                .intervals();
        })
    });
    c.bench_function("query - top level - exclude chars", |b| {
        b.iter(|| {
            let _ = version
                .query()
                .exclude_categories(exclude_categories)
                .exclude_characters(black_box("A@т"))
                .min_codepoint(min_codepoint)
                .max_codepoint(max_codepoint)
                .intervals();
        })
    });
    c.bench_function("query - top level - include and exclude chars", |b| {
        b.iter(|| {
            let _ = version
                .query()
                .exclude_categories(exclude_categories)
                .include_characters(black_box("0123456789"))
                .exclude_characters(black_box("QWERTYUIOP"))
                .min_codepoint(min_codepoint)
                .max_codepoint(max_codepoint)
                .intervals();
        })
    });
    c.bench_function("query - top level - include only", |b| {
        b.iter(|| {
            let _ = version
                .query()
                .include_categories(UnicodeCategory::Ll)
                .include_characters("ABC")
                .min_codepoint(0)
                .max_codepoint(50)
                .intervals();
        })
    });
    c.bench_function("query - single category - codepoint range", |b| {
        b.iter(|| {
            let _ = version
                .query()
                .include_categories(black_box(UnicodeCategory::Lo))
                .max_codepoint(black_box(0x1_0000))
                .intervals();
        })
    });
    let interval_set = UnicodeVersion::V15_0_0
        .query()
        .include_categories(UnicodeCategory::UPPERCASE_LETTER)
        .interval_set()
        .expect("Invalid query input");
    c.bench_function("query - interval set - codepoint_at", |b| {
        b.iter(|| {
            let _ = interval_set.codepoint_at(27);
        })
    });
}

criterion_group!(default, version, intervals, categories, query);
criterion_main!(default);