This is the canonical source repository. Please report issues and submit pull requests through the GitHub repository. https://github.com/RinCatShrine/rcmime
  • Rust 96.9%
  • Python 3.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Rin Cat (鈴猫) a09f301358
feat: extra detect logic and db update
Signed-off-by: Rin Cat (鈴猫) <rincat@rincat.dev>
2026-08-12 01:25:58 +09:00
benches feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
magic feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
src feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
tools feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
.gitignore feat: rcmime 2026-08-11 12:19:58 +09:00
Cargo.lock feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
Cargo.toml feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
clippy.toml feat: rcmime 2026-08-11 12:19:58 +09:00
LICENSE feat: rcmime 2026-08-11 12:19:58 +09:00
README.md feat: extra detect logic and db update 2026-08-12 01:25:58 +09:00
THIRD_PARTY_LICENSES.md feat: rcmime 2026-08-11 12:19:58 +09:00

Rin Cat's MIME

rcmime provides small, dependency-free, cross-platform MIME detection from file content. Filenames and file extensions are never used, and there are no runtime dependencies. The project targets fast, safe, and predictable detection; it trades accuracy and exhaustive validation for bounded work.

What it supports

  • 2,274 signatures covering 901 MIME values.
  • Current IANA-registered names are preferred when they exactly describe the detected format; established unregistered names remain where no exact registered replacement exists.
  • Images, documents, archives, audio, video, fonts, databases, executables, structured text, source code, and plain text.
  • UTF-8, recognized UTF-16, and BOM-marked UTF-32 text.
  • Distinct container formats such as JAR, DOCX, EPUB, and generic ZIP when the content contains enough information to tell them apart.
  • Buffers, seekable readers, and file paths without loading an entire seekable file into memory.
  • Immutable detectors that can be shared across tasks and threads without a lock or queue.

Usage

init() returns the process-wide immutable Detector. Store that reference and reuse its methods across detections, tasks, and threads. The first call initializes the detector; later calls return the same instance.

use std::io::Cursor;

let detector = rcmime::init();

println!("{}", detector.from_file("public/logo.png")?);

let body = b"%PDF-1.7\n";
let mime = detector.from_buf(body)
    .unwrap_or("application/octet-stream");
assert_eq!(mime, "application/pdf");

let mut reader = Cursor::new(b"GIF89a".as_slice());
let mime = detector.from_reader(&mut reader)?
    .unwrap_or("application/octet-stream");
assert_eq!(mime, "image/gif");
# Ok::<(), std::io::Error>(())

Return values

API Recognized Unknown I/O failure
Detector::from_buf, Detector::from_bytes Some(mime) None Not applicable
Detector::from_reader Ok(Some(mime)) Ok(None) Err(io::Error)
Detector::from_file Ok(mime) Ok("application/octet-stream") Err(io::Error)

Detector::from_buf and Detector::from_bytes are equivalent. Detector::from_reader restores the stream's original position before returning.

Bounds and safety

  • Seekable inputs are never loaded completely into memory.
  • Reads and searches are bounded, and offset arithmetic is checked.
  • Detection performs no decompression, regex evaluation, semantic parsing, or runtime recursion.
  • Reader positions are restored even after an I/O error.

There is intentionally no timeout. The fixed search and rule bounds limit work, while cancellation or I/O deadlines remain the caller's responsibility. Detection identifies a media type; it does not prove that a file is safe to render or execute.

Command line

For occasional command-line use, the package also includes a small binary:

rcmime path/to/file
rcmime first-file second-file

From the source tree, install it with cargo install --path . --features cli, or run it without installing with cargo run --features cli -- path/to/file.

One path prints only its MIME value. With multiple paths, each line is printed as path: MIME. The detector is initialized once per invocation. File errors are written to stderr and produce exit code 1; invalid options or missing arguments produce exit code 2. The binary is gated behind the cli feature and is not built for ordinary library builds.

Limits

Some MIME types are unsupported by design, especially when identifying them would require decompression, unbounded searching, or semantic parsing. Results are best-effort hints, not validation: rules inspect only selected parts of the content, so false positives and false negatives are possible. Ambiguous content may return a generic MIME type or no match. Signatures can be spoofed by placing expected bytes at checked locations. A returned MIME type does not establish that the input is a structurally valid file of that format and must not be the sole basis for a security or access control decision.

For example, gzip data returns application/gzip. A .tar.gz also returns application/gzip: detecting the inner TAR stream would require decompression, which is intentionally outside this crate's scope. An uncompressed TAR can be recognized directly from its fixed-offset ustar marker.

Shared container syntax such as JSON, XML, or ZIP is not assigned a more specific MIME type unless the content has a subtype-specific marker.

Quality and performance

A fresh audit of the current detector used 4,335 fixtures from Google Magika, Apache Tika, Apache POI, sindresorhus/file-type, h2non/filetype, gabriel-vasile/mimetype, mmalecot/file-format, Kaitai Struct, and locally generated format-gap fixtures. Of those, 54 incomplete or invalid fixtures were excluded. Among the remaining 4,281 complete samples, there were 4,100 correct detections, including valid MIME aliases (95.77%), 181 degraded detections returning a generic type (4.23%), and 0 incorrect detections (0.00%).

Release-mode results from an AMD Ryzen 9 5950X with Rust 1.97.1:

Input Time per detection
GIF 0.43 µs
PDF 0.52 µs
SVG 3.31 µs
PNG 4.95 µs
Parquet 5.06 µs
HTML 5.25 µs
Matroska 5.29 µs
Zstandard 5.43 µs
JPEG 6.57 µs
age 6.86 µs
WebAssembly 6.94 µs
ORC 7.24 µs
gzip 7.72 µs
WebP 7.98 µs
AVIF 8.51 µs
raw JPEG XL 8.64 µs
Avro 8.64 µs
JSON text 9.83 µs
generic ZIP 11.12 µs
DOCX 11.20 µs
SQLite 15.41 µs
USDZ 24.97 µs
TAR 40.89 µs
HDF5 129.52 µs
1 GiB seekable input 0.54 ms
1 MiB worst-case search 0.51 ms

The first init() took about 0.44 ms; later calls returning the existing detector took about 1 ns. These are local measurements, not performance guarantees.

Size and memory

Incremental cost measured with a minimal x86-64 Linux release program:

Resource Increase
Linked executable About 1.10 MiB
Initialized detector and resident database About 1.80 MiB RSS
Each active detection About 12 KiB stack

Seekable input is not loaded into memory. Actual linked size and resident memory vary by target, linker, allocator, and surrounding application.

License

Project code is Copyright (c) 2026 Rin Cat (鈴猫) rincat@rincat.dev and licensed under the MIT License. The generated database combines project-authored signatures with material derived from separately licensed sources; see the third-party notices.