Mojibake: Unicode 17, Zero Dependencies, One C File
Mojibake is a new C11/C++17 Unicode library that ships as a single .c and .h file with zero external dependencies. It implements Unicode 17.0, including normalization, case conversion, collation, segmentation, bidirectional text, emoji detection, and confusable detection.
Drop-In, No Runtime
Mojibake is designed for projects that want standards-compliant Unicode processing without pulling in ICU or libunistring. The amalgamated mojibake.c and mojibake.h can be added to any C11 or C++17 project with no build system changes. The library passes 1.5M+ assertions, including the official Unicode conformance test suites for supported algorithms.
What It Does
Mojibake covers several Unicode algorithms:
- Normalization: NFC, NFD, NFKC, NFKD with quick-check (
mjb_string_is_normalized) - Case conversion: uppercase, lowercase, titlecase, and case folding (including NFKC casefold)
- Collation: Unicode Collation Algorithm with sort keys (
mjb_collation_key) - Segmentation: grapheme clusters, words, sentences, line breaks (UAX #29 and UAX #14)
- Bidirectional text: full UAX #9 algorithm
- Emoji: codepoint properties, sequence analysis, RGI emoji detection
- Display width: East Asian width and terminal display width with truncation
- Confusable detection: generate skeletons and check visual confusability (UTS #39)
- Identifier validation: XID/ID checks for parsers and compilers (UAX #31)
All functions accept and output UTF-8, UTF-16LE/BE, and UTF-32LE/BE. Encoding detection and conversion are built-in.
Code Example
#include
#include
#include "mojibake.h"
int main() {
const char *input = "Cafe\xCC\x81"; // "Cafe" + combining acute
mjb_result result;
mjb_normalize(input, strlen(input), MJB_ENC_UTF_8, MJB_NORMALIZATION_NFC, MJB_ENC_UTF_8, &result);
printf("NFC: %.*s\n", (int)result.output_size, result.output);
mjb_result_free(&result);
const char *case_input = "Straße";
mjb_nfkc_casefold(case_input, strlen(case_input), MJB_ENC_UTF_8, MJB_ENC_UTF_8, &result);
printf("NFKC casefold: %.*s\n", (int)result.output_size, result.output);
mjb_result_free(&result);
return 0;
}
Output:
NFC: Café
NFKC casefold: strasse
Build-Time Feature Flags
Mojibake allows compiling out optional tables to reduce binary size. For example, disabling character names saves ~30% of table size:
cmake -S . -B build-no-name -DMJB_FEATURE_CHARACTER_NAMES=OFF
cmake --build build-no-name
Other features like collation, bidirectional text, and emoji can be toggled independently.
Embeddable and Tested
- Custom allocators via
mjb_set_memory_functions - C++17 wrapper (
src/cpp/mojibake.hpp) - CLI tool (
src/shell) - WASM + TypeScript API (
src/api) - Fuzzed with libFuzzer, AddressSanitizer, and UBSan clean
Why It Matters
Most C/C++ projects that need Unicode either link against ICU (a heavy dependency) or implement only a subset. Mojibake provides a complete Unicode 17 implementation in two files, making it practical for embedded systems, game engines, or any project that wants to avoid dependency bloat. The library is MIT-licensed.
Editor's Take
I've been burned by ICU's size and complexity in embedded projects. Mojibake's approach—single amalgamation, zero dependencies, and compile-time feature flags—is exactly what C developers need. I appreciate that it passes official conformance tests, not just basic smoke tests. The only downside is that it's new, so community adoption and bug reports are still minimal. If you need Unicode today and can't stomach ICU, Mojibake is worth a look.
Developer Insights
- Use Mojibake when you need full Unicode 17 support without adding a runtime dependency.
- Compile out features you don't need to keep the binary small.
- The WASM build lets you test functions in the browser before integrating.


