OpenViSTAI · Agents · Blockchain · Crypto
Menu
OPENVIST RECIPES

Count ASCII letter digit whitespace and other bytes with Rust

Rust

Summarize an ASCII stream into four disjoint character classes without echoing its content.

Rust code excerpt: Count ASCII letter digit whitespace and other bytes with Rust. Version 0.1.0. Read the full recipe for code, sources and recorded test scope.

Summarize an ASCII stream into four disjoint character classes without echoing its content. Work only with supplied input; this does not contact external services or modify input.

Before you start

  • Save as recipe.rs and compile using Rust 1.99.0: rustc --edition=2021 recipe.rs -o recipe
  • Run recipe without any arguments, including help flags, and supply the complete input through stdin.
  • Reads at most 65537 bytes from stdin and rejects input exceeding 65536 bytes. Accepts empty input. Validates strict UTF-8, rejects a leading UTF-8 BOM, and requires ASCII only before writing successful stdout.
  • Counts ASCII A-Z and a-z as letters, 0-9 as digits, and only bytes 9, 10, 12, 13 and 32 as whitespace. Every other ASCII byte, including byte 11 and NUL, counts as other.
  • Treats stdin as one stream without line splitting, newline normalization or a retained-line count limit. CR and LF each contribute one whitespace byte.
  • Success exits 0 and writes exactly one JSON object followed by a newline, with letters, digits, whitespace and other fields, and no stderr.
  • Invalid arguments, invalid input and non-interrupted stdin-read failures exit 2 without stdout and attempt to write exactly error: invalid input followed by a newline to stderr. Interrupted reads are retried.
  • Uses only the standard library, with no Cargo dependencies, filesystem access, networking, subprocesses or dynamic loading.

Build and check

  1. Save the example as recipe.rs.
  2. Compile with Rust 1.99.0 using rustc --edition=2021 recipe.rs -o recipe.
  3. Prepare UTF-8 input.txt following the documented operation-specific conventions.
  4. Run ./recipe < input.txt.
  5. Inspect the JSON result. Exit 2 with exactly error: invalid input followed by a newline on stderr and no stdout identifies rejected input or stdin-read failure. Output-write failures may also exit nonzero, without that diagnostic and potentially after partial stdout; these are outside the input-error guarantee.

Limits to understand

  • Uses a fixed 65537-byte stack buffer plus constant-size counters and standard I/O overhead. Processing time is linear in the bytes read.
  • Stdin may block while awaiting data or EOF. An input of exactly 65536 bytes requires EOF to establish acceptance; reading a 65537th byte establishes rejection immediately.
  • Successful output does not echo input, but the four counts reveal total length and character-class distribution and may be sensitive.
  • Memory-allocation and output-write failures are outside the input-error guarantee. Successful-output write or flush errors cause exit 2 and may leave partial stdout. A failing stderr write can prevent delivery of the required diagnostic.

Run it

rustc --edition=2021 recipe.rs -o recipe
./recipe < input.txt

Example input

Az09 	
!?

Expected output

{"letters":2,"digits":2,"whitespace":3,"other":2}
INSPECT THE SCRIPT

Inspect, copy and run the documented checks.

This browser preview builds code locally. It does not contact an API or run the code.

Default example ready.

Rust example
use std::io::{self, ErrorKind, Read, Write};

fn invalid_input() -> ! {
    let _ = io::stderr().lock().write_all(b"error: invalid input\n");
    std::process::exit(2);
}

fn read_and_count() -> Result<[usize; 4], ()> {
    let mut buffer = [0u8; 65537];
    let mut length = 0usize;
    let stdin = io::stdin();
    let mut input = stdin.lock();

    loop {
        match input.read(&mut buffer[length..]) {
            Ok(0) => break,
            Ok(n) => {
                length += n;
                if length > 65536 {
                    return Err(());
                }
            }
            Err(error) if error.kind() == ErrorKind::Interrupted => continue,
            Err(_) => return Err(()),
        }
    }

    let bytes = &buffer[..length];
    let text = std::str::from_utf8(bytes).map_err(|_| ())?;
    if bytes.starts_with(&[0xEF, 0xBB, 0xBF]) || !text.is_ascii() {
        return Err(());
    }

    let mut counts = [0usize; 4];
    for &byte in bytes {
        let class = match byte {
            b'A'..=b'Z' | b'a'..=b'z' => 0,
            b'0'..=b'9' => 1,
            9 | 10 | 12 | 13 | 32 => 2,
            _ => 3,
        };
        counts[class] += 1;
    }
    Ok(counts)
}

fn main() {
    if std::env::args_os().nth(1).is_some() {
        invalid_input();
    }

    let counts = match read_and_count() {
        Ok(counts) => counts,
        Err(()) => invalid_input(),
    };

    let stdout = io::stdout();
    let mut output = stdout.lock();
    let result = writeln!(
        output,
        "{{\"letters\":{},\"digits\":{},\"whitespace\":{},\"other\":{}}}",
        counts[0], counts[1], counts[2], counts[3]
    );
    if result.is_err() || output.flush().is_err() {
        std::process::exit(2);
    }
}
What to verify after running
  • The example must produce the documented JSON result.
  • Empty stdin must produce the documented empty result.
  • Reject extra arguments, malformed UTF-8, a leading BOM, oversized input and unreadable stdin with the fixed input-error diagnostic and no stdout.

Request or format preview

Illustrative preview. No provider response, price or balance is fetched.

Change a setting to inspect the preview.

Official sources and related profiles

    Code license: MIT. Keep the license notice with your copy.

    Example version 0.1.0 · Documentation reviewed 2026-10-14.

    Six independent CLI fixtures and 11 supplementary cases are executed in pinned, offline Rust 1.99.0 containers. These checks do not establish exhaustive correctness or behavior on other versions.

    Related browser tools

    Inspect related data locally in your browser. These tools have their own input rules and limits; they do not execute this recipe.