regexr 0.3.2

A high-performance regex engine built from scratch with JIT compilation and SIMD acceleration
Documentation
//! Test data generators for realistic benchmarking scenarios.
//!
//! Sample lists are cycled by a counter. Indexing by `result.len()` settles into
//! a short cycle and silently drops entries.

use std::sync::OnceLock;

/// Truncates to at most `target_size` bytes without splitting a character.
fn truncate_to_chars(text: &mut String, target_size: usize) {
    if text.len() <= target_size {
        return;
    }
    let mut end = target_size;
    while end > 0 && !text.is_char_boundary(end) {
        end -= 1;
    }
    text.truncate(end);
}

/// Cached test data to avoid regeneration overhead
static CACHED_DATA: OnceLock<TestDataCache> = OnceLock::new();

pub struct TestDataCache {
    pub emails_1kb: String,
    pub emails_10kb: String,
    pub emails_100kb: String,
    pub urls_1kb: String,
    pub urls_10kb: String,
    pub urls_100kb: String,
    pub logs_1kb: String,
    pub logs_10kb: String,
    pub logs_100kb: String,
    pub json_1kb: String,
    pub json_10kb: String,
    pub json_100kb: String,
    pub ips_1kb: String,
    pub ips_10kb: String,
    pub ips_100kb: String,
    pub html_1kb: String,
    pub html_10kb: String,
    pub html_100kb: String,
    pub text_1kb: String,
    pub text_10kb: String,
    pub text_100kb: String,
    pub code_1kb: String,
    pub code_10kb: String,
    pub code_100kb: String,
}

/// Get or initialize the test data cache
pub fn get_test_data() -> &'static TestDataCache {
    CACHED_DATA.get_or_init(|| TestDataCache {
        // Email test data
        emails_1kb: generate_email_data(1024),
        emails_10kb: generate_email_data(10 * 1024),
        emails_100kb: generate_email_data(100 * 1024),

        // URL test data
        urls_1kb: generate_url_data(1024),
        urls_10kb: generate_url_data(10 * 1024),
        urls_100kb: generate_url_data(100 * 1024),

        // Log test data
        logs_1kb: generate_log_data(1024),
        logs_10kb: generate_log_data(10 * 1024),
        logs_100kb: generate_log_data(100 * 1024),

        // JSON test data
        json_1kb: generate_json_data(1024),
        json_10kb: generate_json_data(10 * 1024),
        json_100kb: generate_json_data(100 * 1024),

        // IP address test data
        ips_1kb: generate_ip_data(1024),
        ips_10kb: generate_ip_data(10 * 1024),
        ips_100kb: generate_ip_data(100 * 1024),

        // HTML test data
        html_1kb: generate_html_data(1024),
        html_10kb: generate_html_data(10 * 1024),
        html_100kb: generate_html_data(100 * 1024),

        // Plain text test data
        text_1kb: generate_text_data(1024),
        text_10kb: generate_text_data(10 * 1024),
        text_100kb: generate_text_data(100 * 1024),

        // Code test data
        code_1kb: generate_code_data(1024),
        code_10kb: generate_code_data(10 * 1024),
        code_100kb: generate_code_data(100 * 1024),
    })
}

fn generate_email_data(target_size: usize) -> String {
    let valid_emails = [
        "john.doe@example.com",
        "jane_smith@company.org",
        "test+tag@subdomain.example.co.uk",
        "user123@test-domain.com",
        "contact@mysite.io",
        "admin@localhost.localdomain",
    ];

    let invalid_emails = [
        "not-an-email",
        "@example.com",
        "missing@domain",
        "spaces in@email.com",
        "double@@at.com",
    ];

    let mut result = String::with_capacity(target_size);
    let mut toggle = true;
    let mut index = 0usize;

    while result.len() < target_size {
        if toggle {
            result.push_str(valid_emails[index % valid_emails.len()]);
        } else {
            result.push_str(invalid_emails[index % invalid_emails.len()]);
        }
        index += 1;
        result.push_str(" some filler text to pad the data ");
        toggle = !toggle;
    }

    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_url_data(target_size: usize) -> String {
    let urls = [
        "https://www.example.com/path/to/resource",
        "http://subdomain.test.org:8080/api/v1/users?id=123",
        "https://github.com/user/repo/blob/main/src/file.rs",
        "http://localhost:3000/admin/dashboard",
        "https://api.service.io/endpoint?key=value&foo=bar",
    ];

    let mut result = String::with_capacity(target_size);

    for index in 0.. {
        if result.len() >= target_size {
            break;
        }
        result.push_str("Check out this link: ");
        result.push_str(urls[index % urls.len()]);
        result.push_str(" for more information. ");
    }

    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_log_data(target_size: usize) -> String {
    let levels = ["INFO", "WARNING", "ERROR", "CRITICAL", "DEBUG"];
    // The level names appear here in lower case as well as in the `[LEVEL]`
    // field, because the log-severity benchmark searches for them that way. With
    // only the upper-case field to match, that benchmark found nothing at all and
    // was timing a rejection scan rather than a search.
    let messages = [
        "Request processed successfully",
        "Connection timeout after 30s: warning issued to the caller",
        "Database query failed with a fatal error while committing",
        "User authentication successful",
        "Cache miss for key: user_123, critical path fell back to disk",
    ];

    let mut result = String::with_capacity(target_size);
    let mut day = 1;
    let mut hour = 0;
    let mut index = 0usize;

    while result.len() < target_size {
        result.push_str(&format!(
            "2024-01-{:02} {:02}:30:45 [{}] {}\n",
            day,
            hour,
            levels[index % levels.len()],
            messages[index % messages.len()]
        ));
        index += 1;

        hour = (hour + 1) % 24;
        if hour == 0 {
            day = (day % 28) + 1;
        }
    }

    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_json_data(target_size: usize) -> String {
    let mut result = String::with_capacity(target_size);
    let mut counter = 0;

    result.push_str("{\"users\":[");

    while result.len() < target_size - 100 {
        if counter > 0 {
            result.push(',');
        }
        result.push_str(&format!(
            r#"{{"id":{},"name":"User {}","email":"user{}@example.com","status":"active","data":"Some escaped \"quoted\" text here"}}"#,
            counter, counter, counter
        ));
        counter += 1;
    }

    result.push_str("]}");
    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_ip_data(target_size: usize) -> String {
    let mut result = String::with_capacity(target_size);
    let mut octet = 1;

    while result.len() < target_size {
        result.push_str(&format!(
            "Connection from 192.168.{}.{} on port 8080. ",
            (octet / 256) % 256,
            octet % 256
        ));
        octet += 1;
    }

    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_html_data(target_size: usize) -> String {
    let tags = [
        "<div class=\"container\">",
        "<p>Some paragraph text</p>",
        "<a href=\"/link\">Click here</a>",
        "<span style=\"color: red;\">Red text</span>",
        "<img src=\"image.jpg\" alt=\"description\">",
        "</div>",
    ];

    let mut result = String::with_capacity(target_size);
    result.push_str("<!DOCTYPE html><html><body>");

    for index in 0.. {
        if result.len() >= target_size.saturating_sub(100) {
            break;
        }
        result.push_str(tags[index % tags.len()]);
        result.push_str(" Some text content between tags. ");
    }

    result.push_str("</body></html>");
    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_text_data(target_size: usize) -> String {
    let words = [
        "the", "quick", "brown", "fox", "jumps", "over", "lazy", "dog", "and", "then", "runs",
        "through", "forest", "near", "river", "where", "birds", "sing", "their", "morning",
        "songs",
    ];

    let mut result = String::with_capacity(target_size);

    for index in 0.. {
        if result.len() >= target_size {
            break;
        }
        result.push_str(words[index % words.len()]);
        result.push(' ');
    }

    truncate_to_chars(&mut result, target_size);
    result
}

fn generate_code_data(target_size: usize) -> String {
    let code_snippets = [
        r#"let x = "hello world";"#,
        r#"const y = 'single quoted string';"#,
        r#"var z = "escaped \"quotes\" inside";"#,
        r#"String s = "Java string literal";"#,
        r#"str = 'Python string with "nested" quotes';"#,
    ];

    let mut result = String::with_capacity(target_size);

    for index in 0.. {
        if result.len() >= target_size {
            break;
        }
        result.push_str(code_snippets[index % code_snippets.len()]);
        result.push('\n');
    }

    truncate_to_chars(&mut result, target_size);
    result
}

/// Generate multilingual text for Unicode testing
pub fn generate_unicode_data(target_size: usize) -> String {
    let texts = vec![
        "Hello world",      // English
        "Bonjour le monde", // French
        "Hola mundo",       // Spanish
        "Привет мир",       // Russian
        "こんにちは世界",   // Japanese
        "你好世界",         // Chinese
        "مرحبا بالعالم",    // Arabic
        "שלום עולם",        // Hebrew
        "Γειά σου κόσμε",   // Greek
    ];

    let mut result = String::with_capacity(target_size);

    for index in 0.. {
        if result.len() >= target_size {
            break;
        }
        result.push_str(texts[index % texts.len()]);
        result.push_str("");
    }

    truncate_to_chars(&mut result, target_size);
    result
}