Skip to content

MIME Type Reference — C++ source

A searchable map of common file extensions to their MIME types - text, image, audio, video, application, font, and multipart. Look up by extension or by type, filter by category, and copy the exact Content-Type string for any format.

This is the C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.

// mime-types — C++20 port of the curated extension ↔ MIME table, ported from
// src/lib/mime-types.ts. Display source for CosmoDev's polyglot tool pages.
//
// The table is a constexpr array of 71 entries grouped by the seven top-level
// media types (RFC 2046). Each entry's `type` always equals the top-level
// segment of its own `mime`, so the category filter and the Content-Type
// never disagree.

#include <algorithm>
#include <cctype>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

enum class MimeCategory {
    Text, Image, Audio, Video, Application, Font, Multipart,
};

struct MimeEntry {
    std::string_view ext;   // lowercase, no leading dot (e.g. "json")
    std::string_view mime;  // full Content-Type (e.g. "application/json")
    MimeCategory type;      // always the top-level segment of `mime`
    std::string_view name;  // human-readable format name (e.g. "JSON")
};

// Display order of the categories (used by the UI filter).
inline constexpr std::array MIME_CATEGORIES{
    MimeCategory::Text,    MimeCategory::Image,  MimeCategory::Audio,
    MimeCategory::Video,   MimeCategory::Application, MimeCategory::Font,
    MimeCategory::Multipart,
};

// The curated extension ↔ MIME table. One entry is one (extension, type)
// pair, so an extension with several MIME types (e.g. .js) appears more than
// once, and a MIME with several extensions (e.g. image/jpeg) appears once per
// extension. Values follow IANA / MDN conventions.
inline constexpr std::array MIME{
    // ── text ────────────────────────────────────────────────────────────
    MimeEntry{"txt", "text/plain", MimeCategory::Text, "Plain text"},
    MimeEntry{"html", "text/html", MimeCategory::Text, "HTML document"},
    MimeEntry{"htm", "text/html", MimeCategory::Text, "HTML document"},
    MimeEntry{"xhtml", "application/xhtml+xml", MimeCategory::Application, "XHTML"},
    MimeEntry{"css", "text/css", MimeCategory::Text, "CSS stylesheet"},
    MimeEntry{"csv", "text/csv", MimeCategory::Text, "Comma-separated values"},
    MimeEntry{"xml", "application/xml", MimeCategory::Application, "XML document"},
    MimeEntry{"js", "text/javascript", MimeCategory::Text, "JavaScript"},
    MimeEntry{"js", "application/javascript", MimeCategory::Application, "JavaScript (legacy)"},
    MimeEntry{"mjs", "text/javascript", MimeCategory::Text, "JavaScript module"},
    MimeEntry{"json", "application/json", MimeCategory::Application, "JSON"},
    MimeEntry{"md", "text/markdown", MimeCategory::Text, "Markdown"},
    MimeEntry{"yaml", "text/yaml", MimeCategory::Text, "YAML"},
    MimeEntry{"yml", "text/yaml", MimeCategory::Text, "YAML"},
    MimeEntry{"ics", "text/calendar", MimeCategory::Text, "iCalendar"},
    MimeEntry{"rtf", "application/rtf", MimeCategory::Application, "Rich text"},
    MimeEntry{"tsv", "text/tab-separated-values", MimeCategory::Text, "Tab-separated values"},

    // ── image ───────────────────────────────────────────────────────────
    MimeEntry{"jpg", "image/jpeg", MimeCategory::Image, "JPEG image"},
    MimeEntry{"jpeg", "image/jpeg", MimeCategory::Image, "JPEG image"},
    MimeEntry{"png", "image/png", MimeCategory::Image, "PNG image"},
    MimeEntry{"gif", "image/gif", MimeCategory::Image, "GIF image"},
    MimeEntry{"webp", "image/webp", MimeCategory::Image, "WebP image"},
    MimeEntry{"avif", "image/avif", MimeCategory::Image, "AVIF image"},
    MimeEntry{"svg", "image/svg+xml", MimeCategory::Image, "SVG image"},
    MimeEntry{"bmp", "image/bmp", MimeCategory::Image, "Bitmap image"},
    MimeEntry{"ico", "image/x-icon", MimeCategory::Image, "Icon"},
    MimeEntry{"tif", "image/tiff", MimeCategory::Image, "TIFF image"},
    MimeEntry{"tiff", "image/tiff", MimeCategory::Image, "TIFF image"},
    MimeEntry{"heic", "image/heic", MimeCategory::Image, "HEIC image"},

    // ── audio ───────────────────────────────────────────────────────────
    MimeEntry{"mp3", "audio/mpeg", MimeCategory::Audio, "MP3 audio"},
    MimeEntry{"wav", "audio/wav", MimeCategory::Audio, "WAV audio"},
    MimeEntry{"ogg", "audio/ogg", MimeCategory::Audio, "Ogg audio"},
    MimeEntry{"oga", "audio/ogg", MimeCategory::Audio, "Ogg audio"},
    MimeEntry{"flac", "audio/flac", MimeCategory::Audio, "FLAC audio"},
    MimeEntry{"aac", "audio/aac", MimeCategory::Audio, "AAC audio"},
    MimeEntry{"m4a", "audio/mp4", MimeCategory::Audio, "M4A audio"},
    MimeEntry{"weba", "audio/webm", MimeCategory::Audio, "WebM audio"},
    MimeEntry{"mid", "audio/midi", MimeCategory::Audio, "MIDI"},
    MimeEntry{"midi", "audio/midi", MimeCategory::Audio, "MIDI"},

    // ── video ───────────────────────────────────────────────────────────
    MimeEntry{"mp4", "video/mp4", MimeCategory::Video, "MP4 video"},
    MimeEntry{"webm", "video/webm", MimeCategory::Video, "WebM video"},
    MimeEntry{"avi", "video/x-msvideo", MimeCategory::Video, "AVI video"},
    MimeEntry{"mov", "video/quicktime", MimeCategory::Video, "QuickTime video"},
    MimeEntry{"mkv", "video/x-matroska", MimeCategory::Video, "Matroska video"},
    MimeEntry{"mpeg", "video/mpeg", MimeCategory::Video, "MPEG video"},
    MimeEntry{"mpg", "video/mpeg", MimeCategory::Video, "MPEG video"},
    MimeEntry{"ogv", "video/ogg", MimeCategory::Video, "Ogg video"},
    MimeEntry{"m4v", "video/mp4", MimeCategory::Video, "M4V video"},

    // ── application ─────────────────────────────────────────────────────
    MimeEntry{"pdf", "application/pdf", MimeCategory::Application, "PDF document"},
    MimeEntry{"zip", "application/zip", MimeCategory::Application, "ZIP archive"},
    MimeEntry{"gz", "application/gzip", MimeCategory::Application, "Gzip archive"},
    MimeEntry{"tar", "application/x-tar", MimeCategory::Application, "Tar archive"},
    MimeEntry{"rar", "application/vnd.rar", MimeCategory::Application, "RAR archive"},
    MimeEntry{"7z", "application/x-7z-compressed", MimeCategory::Application, "7-Zip archive"},
    MimeEntry{"bin", "application/octet-stream", MimeCategory::Application, "Binary data"},
    MimeEntry{"exe", "application/x-msdownload", MimeCategory::Application, "Windows executable"},
    MimeEntry{"jar", "application/java-archive", MimeCategory::Application, "Java archive"},
    MimeEntry{"wasm", "application/wasm", MimeCategory::Application, "WebAssembly"},
    MimeEntry{"doc", "application/msword", MimeCategory::Application, "Word document"},
    MimeEntry{"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", MimeCategory::Application, "Word document (OOXML)"},
    MimeEntry{"xls", "application/vnd.ms-excel", MimeCategory::Application, "Excel spreadsheet"},
    MimeEntry{"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", MimeCategory::Application, "Excel spreadsheet (OOXML)"},
    MimeEntry{"ppt", "application/vnd.ms-powerpoint", MimeCategory::Application, "PowerPoint"},
    MimeEntry{"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation", MimeCategory::Application, "PowerPoint (OOXML)"},

    // ── font ────────────────────────────────────────────────────────────
    MimeEntry{"woff", "font/woff", MimeCategory::Font, "WOFF font"},
    MimeEntry{"woff2", "font/woff2", MimeCategory::Font, "WOFF2 font"},
    MimeEntry{"ttf", "font/ttf", MimeCategory::Font, "TrueType font"},
    MimeEntry{"otf", "font/otf", MimeCategory::Font, "OpenType font"},
    MimeEntry{"eot", "application/vnd.ms-fontobject", MimeCategory::Application, "Embedded OpenType"},

    // ── multipart ───────────────────────────────────────────────────────
    MimeEntry{"mht", "multipart/related", MimeCategory::Multipart, "MHTML web archive"},
    MimeEntry{"mhtml", "multipart/related", MimeCategory::Multipart, "MHTML web archive"},
};

// Lowercase an extension and drop a leading dot so ".JSON" == "json".
[[nodiscard]] std::string normalizeExt(std::string_view ext)
{
    while (!ext.empty() && std::isspace(static_cast<unsigned char>(ext.front()))) ext.remove_prefix(1);
    while (!ext.empty() && std::isspace(static_cast<unsigned char>(ext.back()))) ext.remove_suffix(1);
    std::string e;
    e.reserve(ext.size());
    for (char c : ext) e.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
    size_t first = e.find_first_not_of('.');
    if (first == std::string::npos) return first == 0 ? e : "";
    return e.substr(first); // drop one or more leading dots, matching /^\.+/
}

namespace detail {
// Trim + lowercase shared by the MIME lookup and the filter query.
[[nodiscard]] std::string canon(std::string_view s)
{
    while (!s.empty() && std::isspace(static_cast<unsigned char>(s.front()))) s.remove_prefix(1);
    while (!s.empty() && std::isspace(static_cast<unsigned char>(s.back()))) s.remove_suffix(1);
    std::string out;
    out.reserve(s.size());
    for (char c : s) out.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
    return out;
}

[[nodiscard]] bool containsI(std::string_view hay, std::string_view needleLower)
{
    if (needleLower.empty()) return true;
    for (size_t i = 0; i + needleLower.size() <= hay.size(); i++) {
        size_t j = 0;
        while (j < needleLower.size() &&
               std::tolower(static_cast<unsigned char>(hay[i + j])) ==
                   static_cast<unsigned char>(needleLower[j]))
            j++;
        if (j == needleLower.size()) return true;
    }
    return false;
}
} // namespace detail

// Every entry whose extension matches. The dot is optional and matching is
// case-insensitive, so ".js", "JS", and "js" are equivalent. Returns {} for
// an unknown/empty extension.
[[nodiscard]] std::vector<MimeEntry> lookupByExt(std::string_view ext)
{
    const std::string e = normalizeExt(ext);
    if (e.empty()) return {};
    std::vector<MimeEntry> out;
    for (const MimeEntry &m : MIME)
        if (m.ext == e) out.push_back(m);
    return out;
}

// Every entry whose MIME matches, case-insensitively, tolerating surrounding
// whitespace. One MIME commonly maps to several extensions (image/jpeg →
// jpg, jpeg), so all are returned. Returns {} for unknown/empty.
[[nodiscard]] std::vector<MimeEntry> lookupByMime(std::string_view mime)
{
    const std::string m = detail::canon(mime);
    if (m.empty()) return {};
    std::vector<MimeEntry> out;
    for (const MimeEntry &e : MIME)
        if (detail::canon(e.mime) == m) out.push_back(e);
    return out;
}

// Optional constraints — nullopt fields are ignored, mirroring `undefined`
// fields in the TypeScript source.
struct MimeFilter {
    std::optional<MimeCategory> type;
    std::string query;
};

// Filter the full table by category and/or a free-text query matched
// (case-insensitively) against ext, mime, and name. An empty query returns
// the category-filtered set unchanged.
[[nodiscard]] std::vector<MimeEntry> filterMime(const MimeFilter &opts = {})
{
    const std::string q = detail::canon(opts.query);
    std::vector<MimeEntry> out;
    for (const MimeEntry &e : MIME) {
        if (opts.type && e.type != *opts.type) continue;
        if (!q.empty() && !detail::containsI(e.ext, q) &&
            !detail::containsI(e.mime, q) && !detail::containsI(e.name, q))
            continue;
        out.push_back(e);
    }
    return out;
}

#include <array>
#include <cstdio>

int main()
{
    for (const MimeEntry &e : lookupByExt(".JSON"))
        std::printf(".JSON -> %.*s (%.*s)\n",
                    static_cast<int>(e.mime.size()), e.mime.data(),
                    static_cast<int>(e.name.size()), e.name.data());
    for (const MimeEntry &e : lookupByMime("IMAGE/JPEG"))
        std::printf("image/jpeg -> .%.*s\n",
                    static_cast<int>(e.ext.size()), e.ext.data());
    std::printf("query \"font\": %zu of %zu entries\n",
                filterMime(MimeFilter{.query = "font"}).size(), MIME.size());
    return 0;
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →