Unicode 17 - C11 - zero dependencies

Unicode text processing,
without the baggage.

Mojibake is a small, fast, self-contained Unicode library for C11 and C++17. Ship standards-compliant text handling without a runtime or dependency tree.

Standard
Unicode 17.0
Runtime
None
License
MIT

Start here

A practical Unicode toolkit

Mojibake is a low-level Unicode 17 text-processing library written in C11 and compatible with C++17. It is released under the MIT License.

It aims to be:

  1. Small
  2. Easy to use
  3. Fast
  4. Self-contained

Mojibake do:

  1. Run in all modern OSes (Linux, macOS, FreeBSD, OpenBSD, NetBSD, Windows 10/11)
  2. Pass the official Unicode test suites for supported algorithms
  3. Implement all Unicode standard algorithms
  4. Satisfy all Unicode Conformance Requirements

Feature highlights

All the C files, together with the Unicode data tables, are concatenated into a single large file and header: mojibake.c and mojibake.h. Zero dependencies.

Text transformation

  • Normalization: NFC/NFD/NFKC/NFKD (mjb_normalize), identifier-oriented NFKC case folding (mjb_nfkc_casefold), plus a fast quick-check (mjb_string_is_normalized) (UAX #15, Unicode 17.0.0)
  • Case conversion: uppercase, lowercase, titlecase, and case folding with full special-casing and conditional mappings (mjb_case)
  • Filtering: strip controls, spaces, or numeric characters while normalizing (mjb_string_filter)

Text analysis

  • Character database: every Unicode Character Database property: category, script and Script_Extensions, block, plane, numeric value, name (mjb_codepoint_character, mjb_codepoint_script_extensions)
  • Segmentation: grapheme clusters, words, sentences, and line-break opportunities (UAX #29, Unicode 17.0.0, UAX #14, Unicode 17.0.0)
  • Bidirectional text: full Unicode Bidirectional Algorithm: paragraph resolution, line reordering, runs (UAX #9, Unicode 17.0.0)
  • Emoji: codepoint properties, sequence analysis, RGI emoji detection
  • Display width: East Asian width and terminal display width, with width-aware truncation (mjb_display_width, mjb_truncate_width)

Sorting and comparison

  • Collation: Unicode Collation Algorithm string comparison and sort keys, in shifted and non-ignorable modes (mjb_string_compare, mjb_collation_key, UTS #10, Unicode 17.0.0)

Security

  • Confusable detection: generate reusable skeletons and check if strings are visually confusable (mjb_confusable_skeleton, mjb_string_is_confusable, UTS #39, Unicode 17.0.0)
  • Identifier validation: XID/ID checks for parser and compiler authors (mjb_string_is_identifier, UAX #31, Unicode 17.0.0)

Integration

  • Encodings: the API accepts and outputs UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE strings, with encoding detection and conversion (mjb_string_encoding, mjb_string_convert_encoding)
  • Parsing and string functions: character-by-character iteration (mjb_next_character) and standard C string.h-style helpers (mjb_string_length, and others)
  • Locales: strict BCP 47 language tag parsing (mjb_locale_parse)
  • Embeddable: custom allocators (mjb_set_memory_functions), build-time feature flags to trim table size, a C++17 wrapper (src/cpp/mojibake.hpp), a CLI tool (src/shell), and a WASM + TypeScript API (src/api)
  • Tested: Mojibake uses Attractor as test suite and run 1.5M+ assertions including the official Unicode conformance suites for supported algorithms
  • Fuzz Mojibake is fuzzed with libFuzzer over untrusted byte input
  • AddressSanitizer and UBSan clean

Usage

You don't need to install anything. Add the C source and header to your build.

  1. Download it here mojibake-amalgamation-026.zip
  2. Unzip it
  3. Add mojibake.c and mojibake.h to your project

Example:

#include <stdio.h>
#include <string.h>
#include "mojibake.h"

// This is a simple example of how to use the Mojibake library.
int main(int argc, char * const argv[]) {
    printf("This is an example of Mojibake v%s\n", mjb_version());
    printf("Unicode version: %s\n", mjb_unicode_version());

    const char *input = "Cafe\xCC\x81";
    mjb_result result;

    if(mjb_normalize(input, strlen(input), MJB_ENC_UTF_8, MJB_NORMALIZATION_NFC, MJB_ENC_UTF_8,
        &result) != MJB_STATUS_OK) {
        return 1;
    }

    // This outputs "NFC: Café", e + ◌́ -> é
    printf("NFC: %.*s\n", (int)result.output_size, result.output);

    if(result.transformed) {
        mjb_free(result.output);
    }

    return 0;
}

Build-time features

Mojibake can compile out optional feature tables to reduce binary size. Feature macros default to enabled.

  • #define MJB_FEATURE_CHARACTER_NAMES controls the Unicode character-name tables used by mjb_codepoint_character(...) to fill mjb_character.name. When disabled, the tables are not compiled and mjb_character.name is reported as Codepoint U+XXXX. This will redude the output of ~30%.

With CMake:

cmake -S . -B build-no-name -DMJB_FEATURE_CHARACTER_NAMES=OFF
cmake --build build-no-name

With the provided Makefile:

make build BUILD_DIR=build-no-name FEATURE_CHARACTER_NAMES=OFF
make test-no-names

API documentation

See API.md or the site for the detailed documentation.

CLI

The src/shell directory builds the mojibake CLI used to test the library. Example usage:

# This outputs "NFC: Café", e + ◌́ -> é
mojibake nfc $'Cafe\u0301'

# The output an emoji sequence [1] Basic, [2] Fully-qualified of two characters U+263A U+FE0F
mojibake emoji "☺️"

Building from source and contributing

See CONTRIBUTING.md for instructions.

Licenses

Mojibake is released under the MIT License (see LICENSE).

Legalese

Here you can find the very detailed and boring informations needed to have this library conformant to the Unicode standard, or at least what I got, at CONFORMANCE_REQUIREMENTS.md

Thanks

Mojibake is built using the work of extraordinary individuals and teams.

  1. Unicode Character Database - Copyright © 1991-2026 Unicode, Inc. (see license.txt)
  2. Unicode CLDR Project - Copyright © 2004-2026 Unicode, Inc. (see LICENSE)

No installation required

Try every function in your browser

Each API reference below includes a live form backed by the WASM build. Expand a function, enter its arguments, and inspect the result immediately.

Download mojibake-wasm-026.zip

Reference

C API

Functions are organized by their metadata section. Select the plus button to see detailed behavior, examples, specifications, and the live WASM form.

API section

Text transformation

Normalize, case-convert, filter, and convert Unicode text.

9 functions

mjb_normalize

Normalize a string to NFC/NFKC/NFD/NFKD form.

mjb_status mjb_normalize(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_normalization form,
    mjb_encoding output_encoding,
    mjb_result *result
);

Normalize a string to the requested Unicode normalization form. If the input is already normalized and no encoding conversion is needed, the input buffer is returned as-is in result->output with result->transformed set to false, without allocating.

Returns

  • MJB_STATUS_OK — The string was normalized (or already normal)
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, or buffer is NULL with a non-zero size
  • MJB_STATUS_INVALID_FORMform is not NFC, NFD, NFKC, or NFKD
  • MJB_STATUS_OVERFLOW — The output size would overflow
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

const char *input = "Cafe\xCC\x81"; // "Cafe" + U+0301 COMBINING ACUTE ACCENT
mjb_result result;

if(mjb_normalize(input, strlen(input), MJB_ENC_UTF_8, MJB_NORMALIZATION_NFC, MJB_ENC_UTF_8,
    &result) != MJB_STATUS_OK) {
    return 1;
}

// NFC: Café
printf("NFC: %.*s", (int)result.output_size, result.output);

if(result.transformed) {
    mjb_free(result.output);
}

Related

Specifications

mjb_string_filter

Filter a string with the selected mjb_filter flags.

mjb_status mjb_string_filter(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_filter filters,
    mjb_encoding output_encoding,
    mjb_result *result
);

MJB_FILTER_LIMIT_COMBINING removes combining marks after the first MJB_FILTER_MAX_COMBINING_MARKS consecutive marks in an emitted run. This is useful for reducing Zalgo-style text while keeping ordinary accents and stacked marks.

Example

const char *mixed_whitespace = "Hello\t\t\n\nworld";
mjb_result result;

if(mjb_string_filter(mixed_whitespace, strlen(mixed_whitespace), MJB_ENC_UTF_8,
    MJB_FILTER_COLLAPSE_SPACES, MJB_ENC_UTF_8, &result) != MJB_STATUS_OK) {
    return 1;
}

// Filtered: Hello world
printf("Filtered: %.*s", (int)result.output_size, result.output);

if(result.transformed) {
    mjb_free(result.output);
}

const char *controls = "\x1\x2\t\n\v\f\r\x1f";

if(mjb_string_filter(controls, strlen(controls), MJB_ENC_UTF_8, MJB_FILTER_CONTROLS,
    MJB_ENC_UTF_8, &result) != MJB_STATUS_OK) {
    return 1;
}

// Filtered: \t\n\v\f\r
printf("Filtered: %.*s", (int)result.output_size, result.output);

if(result.transformed) {
    mjb_free(result.output);
}

Related

filtersThe filters to use. Leave all unchecked for MJB_FILTER_NONE.

mjb_nfkc_casefold

Apply the Unicode NFKC_Casefold transform to a string.

mjb_status mjb_nfkc_casefold(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_encoding output_encoding,
    mjb_result *result
);

Apply the normative NFKC_Casefold mapping and normalize the result to NFC. This transform performs compatibility folding, full default case folding, and removal of default-ignorable codepoints. It is intended for identifier comparison and is not locale-sensitive.

Returns

  • MJB_STATUS_OK — The transformed string was returned
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, or buffer is NULL with a non-zero size
  • MJB_STATUS_OVERFLOW — The output size would overflow
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

const char *input = "Stra\xC3\x9F" "e\xC2\xAD";
mjb_result result;

if(mjb_nfkc_casefold(input, strlen(input), MJB_ENC_UTF_8, MJB_ENC_UTF_8,
    &result) != MJB_STATUS_OK) {
    return 1;
}

// strasse
printf("%.*s", (int)result.output_size, result.output);
mjb_result_free(&result);

Related

Specifications

mjb_codepoint_encode

Encode a codepoint to a string.

unsigned int mjb_codepoint_encode(
    mjb_codepoint codepoint,
    char *buffer,
    size_t byte_length,
    mjb_encoding encoding
);

Example

char encoded[4];
unsigned int size = mjb_codepoint_encode(0x20AC, encoded, sizeof(encoded), MJB_ENC_UTF_8);

// € sign uses 3 UTF-8 bytes
printf("%.*s sign uses %u UTF-8 bytes", (int)size, encoded, size);

mjb_string_convert_encoding

Convert from one encoding to another.

mjb_status mjb_string_convert_encoding(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_encoding output_encoding,
    mjb_result *result
);

Convert a string between the supported encodings (UTF-8, UTF-16LE/BE, UTF-32LE/BE). Generic UTF-16/UTF-32 input consumes a leading BOM as the encoding scheme signature and uses it to resolve byte order. Explicit-endian input preserves an initial U+FEFF as text. Generic UTF-16/UTF-32 without a BOM, and generic UTF-16/UTF-32 output, are rejected because the byte order is not specified.

Returns

  • MJB_STATUS_OK — The string was converted
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, buffer is NULL with a non-zero size, or the input is not valid in the source encoding
  • MJB_STATUS_INVALID_ENCODING — A generic UTF-16/UTF-32 encoding did not provide enough byte order information
  • MJB_STATUS_UNSUPPORTED — The requested encoding conversion is not supported
  • MJB_STATUS_OVERFLOW — The output size would overflow
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

const char *input = "caf\xC3\xA9";
mjb_result result;

if(mjb_string_convert_encoding(input, strlen(input), MJB_ENC_UTF_8,
    MJB_ENC_UTF_16LE, &result) != MJB_STATUS_OK) {
    return 1;
}

// UTF-16LE bytes: 8
printf("UTF-16LE bytes: %zu", result.output_size);
mjb_result_free(&result);

Related

mjb_case

Change string case.

mjb_status mjb_case(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_case_type type,
    mjb_encoding output_encoding,
    mjb_result *result
);

Convert a string to uppercase, lowercase, titlecase, or its case-folded form. Full case mappings are applied, including special casing and conditional mappings, so the output may have a different length than the input. Titlecase uses UAX #29 word boundaries: the first cased character in each word segment is titlecased, and subsequent characters in that segment are lowercased. Casing is tailored by the process-global locale set with mjb_locale_set: the default MJB_LOCALE_EN uses default non-Turkic mappings. MJB_LOCALE_TR and MJB_LOCALE_AZ apply Turkish/Azerbaijani dotted-I casing and Turkic T case-folding mappings. MJB_LOCALE_LT applies Lithuanian dot-above casing rules, while case folding remains the default non-Turkic mapping.

Returns

  • MJB_STATUS_OK — The case conversion succeeded
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, buffer is NULL with a non-zero size, or type is not a valid case type
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

const char *input = "Stra\xC3\x9F""e"; // "Straße"
mjb_result result;

if(mjb_case(input, strlen(input), MJB_ENC_UTF_8, MJB_CASE_UPPER, MJB_ENC_UTF_8,
    &result) != MJB_STATUS_OK) {
    return 1;
}

// Upper: STRASSE
printf("Upper: %.*s", (int)result.output_size, result.output);

if(result.transformed) {
    mjb_free(result.output);
}

Related

Specifications

mjb_codepoint_to_lowercase

Return the codepoint lowercase codepoint.

mjb_codepoint mjb_codepoint_to_lowercase(
    mjb_codepoint codepoint
);

Return the lowercase codepoint of a codepoint. If the codepoint has no lowercase equivalent, the original codepoint is returned.

Returns

  • codepoint — The lowercase codepoint, or the original codepoint

Example

mjb_codepoint codepoint;

codepoint = mjb_codepoint_to_lowercase(0x0041); // U+0041 = 'A'

// A > a
printf("%c > %c", 'A', codepoint);

codepoint = mjb_codepoint_to_lowercase(0x03A3); // U+03A3 = 'Σ'

// U+03A3 > U+03C3, Σ > σ
printf("U+%04X > U+%04X, %s > %s",  0x03A3, codepoint, "Σ", "σ");

Related

mjb_codepoint_to_uppercase

Return the codepoint uppercase codepoint.

mjb_codepoint mjb_codepoint_to_uppercase(
    mjb_codepoint codepoint
);

Return the uppercase codepoint of a codepoint. If the codepoint has no uppercase equivalent, the original codepoint is returned.

Returns

  • codepoint — The uppercase codepoint, or the original codepoint

Example

mjb_codepoint uppercase = mjb_codepoint_to_uppercase(0x00E9); // é

// Uppercase: U+00C9
printf("Uppercase: U+%04X", uppercase);

Related

mjb_codepoint_to_titlecase

Return the codepoint titlecase codepoint.

mjb_codepoint mjb_codepoint_to_titlecase(
    mjb_codepoint codepoint
);

Return the titlecase codepoint of a codepoint. If the codepoint has no titlecase equivalent, the original codepoint is returned.

Returns

  • codepoint — The titlecase codepoint, or the original codepoint

Example

mjb_codepoint titlecase = mjb_codepoint_to_titlecase(0x01F3); // dz

// Titlecase: U+01F2
printf("Titlecase: U+%04X", titlecase);

Related

API section

Text analysis

Inspect codepoints, properties, boundaries, width, emoji, and bidirectional text.

31 functions

mjb_codepoint_character

Return the codepoint character.

mjb_status mjb_codepoint_character(
    mjb_codepoint codepoint,
    mjb_character *character
);

Fill character with the Unicode Character Database record of a codepoint: name, category, combining class, bidirectional category, decomposition, numeric values, mirrored flag, and simple case mappings. When the library is compiled with MJB_FEATURE_CHARACTER_NAMES=OFF the name field is reported as Codepoint U+XXXX.

Returns

  • MJB_STATUS_OK — The character was found and filled
  • MJB_STATUS_INVALID_ARGUMENTcharacter is NULL or the codepoint is not valid
  • MJB_STATUS_NOT_FOUND — The codepoint is not assigned

Example

mjb_character character;

if(mjb_codepoint_character(0x022A, &character) != MJB_STATUS_OK) {
    return 1;
}

// U+022A lowercase: U+022B
printf("U+%04X lowercase: U+%04X", character.codepoint, character.lowercase);

Related

Specifications

mjb_string_is_normalized

Check if a string is normalized to NFC/NFKC/NFD/NFKD form.

mjb_quick_check_result mjb_string_is_normalized(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_normalization form
);

Run the normalization quick-check on a string without allocating. MJB_QC_MAYBE means the string may still be normalized, and only a full normalization pass with mjb_normalize can decide.

Returns

  • MJB_QC_YES — The string is normalized to the requested form
  • MJB_QC_NO — The string is not normalized
  • MJB_QC_MAYBE — Inconclusive: a full normalization is needed to decide

Example

const char *input = "caf\xC3\xA9";
mjb_quick_check_result check = mjb_string_is_normalized(input, strlen(input),
    MJB_ENC_UTF_8, MJB_NORMALIZATION_NFC);

// NFC normalized: yes
printf("NFC normalized: %s", check == MJB_QC_YES ? "yes" : "no");

Related

Specifications

mjb_string_encoding

Return the string encoding (the most probable).

mjb_encoding mjb_string_encoding(
    const char *buffer,
    size_t byte_length
);

mjb_string_encoding reports BOM-derived UTF-16/UTF-32 schemes with the generic family bit plus the resolved endian bit. Passing that detected value consumes the leading BOM as a signature. Passing an explicit-endian encoding such as MJB_ENC_UTF_16BE preserves an initial U+FEFF as text. When flags overlap, as with a UTF-32LE BOM that also has the UTF-16LE BOM prefix, decoding gives UTF-32 precedence.

Example

const char utf16le[] = "\xFF\xFEH\0i\0";
mjb_encoding detected = mjb_string_encoding(utf16le, sizeof(utf16le) - 1);
bool is_utf16le = detected == (MJB_ENC_UTF_16 | MJB_ENC_UTF_16LE);

// UTF-16LE detected: yes
printf("UTF-16LE detected: %s", is_utf16le ? "yes" : "no");

mjb_string_is_ascii

Return true if the string is encoded in ASCII.

bool mjb_string_is_ascii(
    const char *buffer,
    size_t byte_length
);

Example

const char *input = "Plain ASCII";

// ASCII: yes
printf("ASCII: %s", mjb_string_is_ascii(input, strlen(input)) ? "yes" : "no");

mjb_string_is_utf8

Return true if the string is encoded in UTF-8.

bool mjb_string_is_utf8(
    const char *buffer,
    size_t byte_length
);

Example

const char *input = "caf\xC3\xA9";

// Valid UTF-8: yes
printf("Valid UTF-8: %s", mjb_string_is_utf8(input, strlen(input)) ? "yes" : "no");

mjb_string_is_utf16

Return true if the string is encoded in UTF-16BE or UTF-16LE.

bool mjb_string_is_utf16(
    const char *buffer,
    size_t byte_length
);

Example

const char utf16be[] = "\xFE\xFF\0H\0i"; // BOM + "Hi" in UTF-16BE

// UTF-16: yes
printf("UTF-16: %s", mjb_string_is_utf16(utf16be, sizeof(utf16be) - 1) ? "yes" : "no");

mjb_string_length

Return the length of a string.

size_t mjb_string_length(
    const char *buffer,
    size_t max_length,
    mjb_encoding encoding
);

Return the number of Unicode codepoints in a string, up to max_length bytes.

Example

// The "Héllö" string is five Unicode characters, but has different byte lengths in different encodings.

const char *utf8 = "H\xC3\xA9ll\xC3\xB6"; // 7 bytes
const char utf16le[] = "H\0\xE9\0l\0l\0\xF6\0"; // 10 bytes
const char utf16be[] = "\0H\0\xE9\0l\0l\0\xF6"; // 10 bytes
const char utf32le[] = "H\0\0\0\xE9\0\0\0l\0\0\0l\0\0\0\xF6\0\0\0"; // 20 bytes
const char utf32be[] = "\0\0\0H\0\0\0\xE9\0\0\0l\0\0\0l\0\0\0\xF6"; // 20 bytes

// 5 UTF-8 characters
printf("%zu UTF-8 characters", mjb_string_length(utf8, 7, MJB_ENC_UTF_8));
// 5 UTF-16LE characters
printf("%zu UTF-16LE characters", mjb_string_length(utf16le, 10, MJB_ENC_UTF_16LE));
// 5 UTF-16BE characters
printf("%zu UTF-16BE characters", mjb_string_length(utf16be, 10, MJB_ENC_UTF_16BE));
// 5 UTF-32LE characters
printf("%zu UTF-32LE characters", mjb_string_length(utf32le, 20, MJB_ENC_UTF_32LE));
// 5 UTF-32BE characters
printf("%zu UTF-32BE characters", mjb_string_length(utf32be, 20, MJB_ENC_UTF_32BE));

mjb_string_each_character

Run a callback for each character of a string.

mjb_status mjb_string_each_character(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_string_each_character_fn callback
);

Example

mjb_status status = mjb_string_each_character("ABC", 3, MJB_ENC_UTF_8, NULL);

// A callback is required: yes
bool callback_required = status == MJB_STATUS_INVALID_ARGUMENT;

// A callback is required: yes
printf("A callback is required: %s", callback_required ? "yes" : "no");

mjb_codepoint_property_binary

Return the value of a binary Unicode property.

mjb_status mjb_codepoint_property_binary(
    mjb_codepoint codepoint,
    mjb_property property,
    bool *value
);

Return true when the codepoint has the binary property and false when it does not. Passing an enumerated property is a type mismatch and returns MJB_STATUS_INVALID_ARGUMENT.

Example

bool is_alphabetic;

if(mjb_codepoint_property_binary('A', MJB_PR_ALPHABETIC,
    &is_alphabetic) != MJB_STATUS_OK) {
    return 1;
}

// U+0041 is alphabetic: yes
printf("U+0041 is alphabetic: %s", is_alphabetic ? "yes" : "no");

Related

Specifications

mjb_codepoint_property_int

Return the value of an enumerated or integer Unicode property.

mjb_status mjb_codepoint_property_int(
    mjb_codepoint codepoint,
    mjb_property property,
    int32_t *value
);

Passing a binary property is a type mismatch and returns MJB_STATUS_INVALID_ARGUMENT. MJB_STATUS_NOT_FOUND means that the codepoint has no stored value for the requested property.

Example

int32_t script;

if(mjb_codepoint_property_int('A', MJB_PR_SCRIPT, &script) != MJB_STATUS_OK) {
    return 1;
}

// U+0041 uses the Latin script: yes
printf("U+0041 uses the Latin script: %s", script == MJB_SC_LATN ? "yes" : "no");

Related

Specifications

mjb_codepoint_numeric_value

Return the numeric value of a codepoint.

mjb_status mjb_codepoint_numeric_value(
    mjb_codepoint codepoint,
    mjb_numeric_value *value
);

Return the numeric value of a codepoint, if any. If the codepoint has no numeric value, value->decimal and value->digit are set to MJB_NUMBER_NOT_VALID (-1).

Returns

  • MJB_STATUS_OK — The character was found and filled
  • MJB_STATUS_INVALID_ARGUMENTvalue is NULL or the codepoint is not valid

Example

mjb_numeric_value num;

if(mjb_codepoint_numeric_value(0x0031, &num) != MJB_STATUS_OK) { // U+0031 = 1
    return 1;
}

// decimal=1, digit=1, numeric=1
printf("decimal=%d, digit=%d, numeric=%s", num.decimal, num.digit, num.numeric);

if(mjb_codepoint_numeric_value(0x00BD, &num) != MJB_STATUS_OK) { // U+00BD = '½'
    return 1;
}

// decimal=-1, digit=-1, numeric=1/2
printf("decimal=%d, digit=%d, numeric=%s", num.decimal, num.digit, num.numeric);

Specifications

mjb_codepoint_block

Return the character block.

mjb_status mjb_codepoint_block(
    mjb_codepoint codepoint,
    mjb_block_info *block
);

Example

mjb_block_info block;

if(mjb_codepoint_block('A', &block) != MJB_STATUS_OK) {
    return 1;
}

// Block: Basic Latin
printf("Block: %s", block.name);

Specifications

mjb_codepoint_script

Return the script of a codepoint.

mjb_script mjb_codepoint_script(
    mjb_codepoint codepoint
);

Example

mjb_script script = mjb_codepoint_script(0x03A9); // Greek capital omega

// Greek script: yes
printf("Greek script: %s", script == MJB_SC_GREK ? "yes" : "no");

Specifications

mjb_codepoint_script_extensions

Return the Script_Extensions set of a codepoint.

mjb_status mjb_codepoint_script_extensions(
    mjb_codepoint codepoint,
    mjb_script *scripts,
    size_t *count
);

Return the explicit Script_Extensions set, or the ordinary Script value when the codepoint has no explicit Script_Extensions entry. Call first with scripts set to NULL to obtain the required count.

Example

size_t count = 0;

if(mjb_codepoint_script_extensions(0x30FC, NULL, &count) != MJB_STATUS_OK) {
    return 1;
}

mjb_script scripts[3];

if(count > 3 || mjb_codepoint_script_extensions(0x30FC, scripts,
    &count) != MJB_STATUS_OK) {
    return 1;
}

// U+30FC has 2 Script_Extensions
printf("U+30FC has %zu Script_Extensions", count);

Related

Specifications

mjb_codepoint_is_valid

Return true if the codepoint is valid.

bool mjb_codepoint_is_valid(
    mjb_codepoint codepoint
);

Example

// U+10FFFD valid: yes
printf("U+10FFFD valid: %s", mjb_codepoint_is_valid(0x10FFFD) ? "yes" : "no");

mjb_codepoint_is_graphic

Return true if the codepoint is graphic.

bool mjb_codepoint_is_graphic(
    mjb_codepoint codepoint
);

Example

// Letter A is graphic: yes
printf("Letter A is graphic: %s", mjb_codepoint_is_graphic('A') ? "yes" : "no");

mjb_codepoint_is_combining

Return true if the codepoint is combining.

bool mjb_codepoint_is_combining(
    mjb_codepoint codepoint
);

Example

// U+0301 is combining: yes
printf("U+0301 is combining: %s", mjb_codepoint_is_combining(0x0301) ? "yes" : "no");

mjb_codepoint_is_cjk_ideograph

Return if the codepoint is CJK ideograph.

bool mjb_codepoint_is_cjk_ideograph(
    mjb_codepoint codepoint
);

Example

// U+4E00 is a CJK ideograph: yes
printf("U+4E00 is a CJK ideograph: %s", mjb_codepoint_is_cjk_ideograph(0x4E00) ? "yes" : "no");

mjb_codepoint_is_cjk_ext

Return if the codepoint is CJK extension.

bool mjb_codepoint_is_cjk_ext(
    mjb_codepoint codepoint
);

Example

// U+20000 is a CJK extension ideograph: yes
printf("U+20000 is a CJK extension ideograph: %s", mjb_codepoint_is_cjk_ext(0x20000) ? "yes" : "no");

mjb_category_is_graphic

Return true if the category is graphic.

bool mjb_category_is_graphic(
    mjb_category category
);

Example

// Uppercase letters are graphic: yes
bool graphic = mjb_category_is_graphic(MJB_CATEGORY_LU);

// Uppercase letters are graphic: yes
printf("Uppercase letters are graphic: %s", graphic ? "yes" : "no");

mjb_category_is_combining

Return true if the category is combining.

bool mjb_category_is_combining(
    mjb_category category
);

Example

// Nonspacing marks are combining: yes
bool combining = mjb_category_is_combining(MJB_CATEGORY_MN);

// Nonspacing marks are combining: yes
printf("Nonspacing marks are combining: %s", combining ? "yes" : "no");

mjb_codepoint_is_id_start

Return true if the codepoint is a valid Unicode identifier start (Unicode 17.0.0 UAX #31 ID_Start).

bool mjb_codepoint_is_id_start(
    mjb_codepoint codepoint
);

Example

// Greek alpha starts an identifier: yes
bool starts = mjb_codepoint_is_id_start(0x03B1);

// Greek alpha starts an identifier: yes
printf("Greek alpha starts an identifier: %s", starts ? "yes" : "no");

Specifications

mjb_codepoint_is_id_continue

Return true if the codepoint is a valid Unicode identifier continuation (Unicode 17.0.0 UAX #31 ID_Continue).

bool mjb_codepoint_is_id_continue(
    mjb_codepoint codepoint
);

Example

// Digit 7 continues an identifier: yes
bool continues = mjb_codepoint_is_id_continue('7');

// Digit 7 continues an identifier: yes
printf("Digit 7 continues an identifier: %s", continues ? "yes" : "no");

Specifications

mjb_codepoint_is_xid_continue

Return true if the codepoint is a valid NFKC identifier continuation (Unicode 17.0.0 UAX #31 XID_Continue).

bool mjb_codepoint_is_xid_continue(
    mjb_codepoint codepoint
);

Example

// Underscore is XID_Continue: yes
bool continues = mjb_codepoint_is_xid_continue('_');

// Underscore is XID_Continue: yes
printf("Underscore is XID_Continue: %s", continues ? "yes" : "no");

Specifications

mjb_codepoint_is_pattern_syntax

Return true if the codepoint is reserved for use in patterns (Unicode 17.0.0 UAX #31 Pattern_Syntax).

bool mjb_codepoint_is_pattern_syntax(
    mjb_codepoint codepoint
);

Example

// Plus sign is Pattern_Syntax: yes
bool syntax = mjb_codepoint_is_pattern_syntax('+');

// Plus sign is Pattern_Syntax: yes
printf("Plus sign is Pattern_Syntax: %s", syntax ? "yes" : "no");

Specifications

mjb_codepoint_is_pattern_white_space

Return true if the codepoint is pattern whitespace (Unicode 17.0.0 UAX #31 Pattern_White_Space).

bool mjb_codepoint_is_pattern_white_space(
    mjb_codepoint codepoint
);

Example

// Space is Pattern_White_Space: yes
bool whitespace = mjb_codepoint_is_pattern_white_space(' ');

// Space is Pattern_White_Space: yes
printf("Space is Pattern_White_Space: %s", whitespace ? "yes" : "no");

Specifications

mjb_codepoint_is_extended_pictographic

Return true if the codepoint has the Unicode Extended_Pictographic property.

bool mjb_codepoint_is_extended_pictographic(
    mjb_codepoint codepoint
);

Example

// Red heart is Extended_Pictographic: yes
bool pictographic = mjb_codepoint_is_extended_pictographic(0x2764);

// Red heart is Extended_Pictographic: yes
printf("Red heart is Extended_Pictographic: %s", pictographic ? "yes" : "no");

Specifications

mjb_codepoint_plane

Return the plane of the codepoint.

mjb_plane mjb_codepoint_plane(
    mjb_codepoint codepoint
);

Example

mjb_plane plane = mjb_codepoint_plane(0x1F600);

// U+1F600 is in the SMP: yes
printf("U+1F600 is in the SMP: %s", plane == MJB_PLANE_SMP ? "yes" : "no");

mjb_plane_is_valid

Return true if the plane is valid.

bool mjb_plane_is_valid(
    mjb_plane plane
);

Example

// Plane 16 is valid: yes
printf("Plane 16 is valid: %s", mjb_plane_is_valid(MJB_PLANE_PUA_B) ? "yes" : "no");

mjb_plane_name

Return the name of a plane, NULL if the plane specified is not valid.

const char *mjb_plane_name(
    mjb_plane plane,
    bool abbreviation
);

Example

// Plane: Basic Multilingual Plane
printf("Plane: %s", mjb_plane_name(MJB_PLANE_BMP, false));

API section

Sorting and comparison

Compare and sort strings with the Unicode Collation Algorithm.

2 functions

mjb_string_compare

Compare two strings using UCA.

int mjb_string_compare(
    const char *s1,
    size_t s1_byte_length,
    mjb_encoding s1_encoding,
    const char *s2,
    size_t s2_byte_length,
    mjb_encoding s2_encoding,
    mjb_collation_mode mode
);

Compare two strings using the Unicode Collation Algorithm and the default collation element table (DUCET), with strcmp-style semantics.

Returns

  • < 0 — The first string collates before the second
  • 0 — The strings are equal under UCA
  • > 0 — The first string collates after the second

Example

int order = mjb_string_compare("apple", 5, MJB_ENC_UTF_8,
    "banana", 6, MJB_ENC_UTF_8, MJB_COLLATION_NON_IGNORABLE);

// apple sorts before banana: yes
printf("apple sorts before banana: %s", order < 0 ? "yes" : "no");

Related

Specifications

mjb_collation_key

Generate a UCA sort key for a string.

mjb_status mjb_collation_key(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_collation_mode mode,
    mjb_result *result
);

Generate a binary sort key for a string. Sort keys of different strings can be compared with memcmp and yield the same order as mjb_string_compare. Useful when the same strings are compared many times, such as sorting or database indexing.

Returns

  • MJB_STATUS_OK — The sort key was generated
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, or buffer is NULL with a non-zero size
  • MJB_STATUS_OVERFLOW — The sort key size would overflow
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

mjb_result key;

if(mjb_collation_key("r\xC3\xA9sum\xC3\xA9", 8, MJB_ENC_UTF_8,
    MJB_COLLATION_NON_IGNORABLE, &key) != MJB_STATUS_OK) {
    return 1;
}

// Sort key is non-empty: yes
printf("Sort key is non-empty: %s", key.output_size > 0 ? "yes" : "no");
mjb_result_free(&key);

Related

Specifications

API section

Security

Detect visually confusable Unicode text.

3 functions

mjb_string_is_identifier

Return true if the string is a valid Unicode identifier (Unicode 17.0.0 UAX #31).

bool mjb_string_is_identifier(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_identifier_profile profile
);

Validate a string as a Unicode identifier: the first character must be a valid identifier start and the following ones valid identifier continuations, using ID_Start/ID_Continue for the DEFAULT profile or XID_Start/XID_Continue for the NFKC profile.

Example

const char *identifier = "delta_2";

bool valid = mjb_string_is_identifier(identifier, strlen(identifier), MJB_ENC_UTF_8,
    MJB_IDENTIFIER_NFKC);

// Valid identifier: yes
printf("Valid identifier: %s", valid ? "yes" : "no");

Related

Specifications

mjb_confusable_skeleton

Compute a Unicode confusable skeleton (Unicode 17.0.0 UTS #39 Section 4).

mjb_status mjb_confusable_skeleton(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_encoding output_encoding,
    mjb_result *result
);

Compute the UTS #39 bidiSkeleton(LTR, input): apply the Unicode Bidirectional Algorithm through L4, then NFD, remove default-ignorables, substitute prototypes from confusables.txt, and reapply NFD. Skeletons can be stored or indexed so future confusable checks can compare them directly.

Returns

  • MJB_STATUS_OK — The confusable skeleton was returned
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, or buffer is NULL with a non-zero size
  • MJB_STATUS_OVERFLOW — The output size would overflow
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

const char *input = "h\xD0\xB5llo"; // Cyrillic U+0435 in place of e
mjb_result result;

if(mjb_confusable_skeleton(input, strlen(input), MJB_ENC_UTF_8, MJB_ENC_UTF_8,
    &result) != MJB_STATUS_OK) {
    return 1;
}

// hello
printf("%.*s", (int)result.output_size, result.output);
mjb_result_free(&result);

Related

Specifications

mjb_string_is_confusable

Return true if two strings are visually confusable (Unicode 17.0.0 UTS #39 Section 4): skeleton(s1) == skeleton(s2).

bool mjb_string_is_confusable(
    const char *s1,
    size_t s1_byte_length,
    mjb_encoding s1_encoding,
    const char *s2,
    size_t s2_byte_length,
    mjb_encoding s2_encoding
);

Compute the confusable skeleton of both strings and return true when the skeletons are equal, meaning the two strings are visually confusable, such as "good" and "gооd" with Cyrillic о.

Example

const char *latin = "hello";
const char *mixed = "h\xD0\xB5llo"; // Cyrillic е

bool confusable = mjb_string_is_confusable(latin, strlen(latin), MJB_ENC_UTF_8,
    mixed, strlen(mixed), MJB_ENC_UTF_8);

// Visually confusable: yes
printf("Visually confusable: %s", confusable ? "yes" : "no");

Related

Specifications

API section

Segmentation

Segment text into grapheme clusters, words, and sentences.

7 functions

mjb_break_line

Unicode line break algorithm.

mjb_break_type mjb_break_line(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_next_line_state *state
);

Example

mjb_next_line_state state;
state.index = 0;
mjb_break_type type = mjb_break_line("Hello world", 11, MJB_ENC_UTF_8, &state);

// First line-break result is set: yes
printf("First line-break result is set: %s", type != MJB_BT_NOT_SET ? "yes" : "no");

Related

Specifications

mjb_break_word

Word cluster breaking.

mjb_break_type mjb_break_word(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_next_word_state *state
);

Example

mjb_next_word_state state;
state.index = 0;
size_t boundaries = 0;

while(mjb_break_word("Hello world", 11, MJB_ENC_UTF_8, &state) != MJB_BT_NOT_SET) {
    ++boundaries;
}

// Word-break positions: 11
printf("Word-break positions: %zu", boundaries);

Related

Specifications

mjb_break_sentence

Sentence boundaries breaking.

mjb_break_type mjb_break_sentence(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_next_sentence_state *state
);

Example

mjb_next_sentence_state state;
state.index = 0;
size_t boundaries = 0;
const char *input = "Hello. Goodbye.";

while(mjb_break_sentence(input, strlen(input), MJB_ENC_UTF_8, &state) != MJB_BT_NOT_SET) {
    ++boundaries;
}

// Sentence-break positions: 15
printf("Sentence-break positions: %zu", boundaries);

Related

Specifications

mjb_break_grapheme_cluster

Grapheme cluster breaking.

mjb_break_type mjb_break_grapheme_cluster(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_next_state *state
);

Iterate the grapheme cluster (user-perceived character) boundaries of a string. Call repeatedly with the same state until it reports the end of the string.

Example

const char *input = "e\xCC\x81"; // e + combining acute accent
mjb_next_state state;
state.index = 0;
size_t codepoints = 0;

while(mjb_break_grapheme_cluster(input, strlen(input), MJB_ENC_UTF_8,
    &state) != MJB_BT_NOT_SET) {
    ++codepoints;
}

// Codepoints examined: 2
printf("Codepoints examined: %zu", codepoints);

Related

Specifications

mjb_truncate

Return the number of bytes that form the first `max_graphemes` grapheme cluster segments.

size_t mjb_truncate(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    size_t max_graphemes
);

Example

const char *input = "A\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9Z"; // A🇮🇹Z
size_t bytes = mjb_truncate(input, strlen(input), MJB_ENC_UTF_8, 2);

// First two graphemes use 9 bytes
printf("First two graphemes use %zu bytes", bytes);

mjb_truncate_word

Return the number of bytes that form the first max_segments word-break segments.

size_t mjb_truncate_word(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    size_t max_segments
);

Example

const char *input = "Hello world";
size_t bytes = mjb_truncate_word(input, strlen(input), MJB_ENC_UTF_8, 1);

// First word segment uses 5 bytes
printf("First word segment uses %zu bytes", bytes);

mjb_truncate_word_width

Return the number of bytes whose word-break segments fit within max_columns display columns.

size_t mjb_truncate_word_width(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_width_context context,
    size_t max_columns
);

Example

const char *input = "Hello world";
size_t bytes = mjb_truncate_word_width(input, strlen(input), MJB_ENC_UTF_8,
    MJB_WIDTH_CONTEXT_WESTERN, 6);

// Six columns include 6 bytes
printf("Six columns include %zu bytes", bytes);

API section

Bidirectional text

Resolve embedding levels, reorder lines, and get line runs for bidirectional text.

4 functions

mjb_bidi_resolve

Resolve bidirectional text (TR9) for a paragraph.

mjb_status mjb_bidi_resolve(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_direction direction,
    mjb_bidi_paragraph *result
);

Resolve the embedding levels of a paragraph following the Unicode Bidirectional Algorithm. The resolved paragraph can then be split into lines and reordered visually with mjb_bidi_reorder_line and mjb_bidi_line_runs.

Returns

  • MJB_STATUS_OK — The paragraph was resolved
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL, or buffer is NULL with a non-zero size
  • MJB_STATUS_OVERFLOW — The paragraph size would overflow
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

const char *input = "abc \xD7\x90\xD7\x91\xD7\x92"; // abc אבג
mjb_bidi_paragraph paragraph;

if(mjb_bidi_resolve(input, strlen(input), MJB_ENC_UTF_8, MJB_DIRECTION_AUTO,
    &paragraph) != MJB_STATUS_OK) {
    return 1;
}

// Paragraph codepoints: 7
printf("Paragraph codepoints: %zu", paragraph.count);
mjb_bidi_free(&paragraph);

Related

Specifications

mjb_bidi_reorder_line

Reorder a line visually (L1-L4); visual_order is caller-allocated.

mjb_status mjb_bidi_reorder_line(
    const mjb_bidi_paragraph *paragraph,
    size_t line_start,
    size_t line_end,
    size_t *visual_order
);

Example

const char *input = "\xD7\x90\xD7\x91\xD7\x92"; // אבג
mjb_bidi_paragraph paragraph;
size_t visual_order[3];

if(mjb_bidi_resolve(input, strlen(input), MJB_ENC_UTF_8, MJB_DIRECTION_AUTO,
    &paragraph) != MJB_STATUS_OK ||
    mjb_bidi_reorder_line(&paragraph, 0, paragraph.count,
        visual_order) != MJB_STATUS_OK) {
    return 1;
}

// First visual index: 2
printf("First visual index: %zu", visual_order[0]);
mjb_bidi_free(&paragraph);

Related

Specifications

mjb_bidi_line_runs

Compute visual level runs; pass runs=NULL to count first.

mjb_status mjb_bidi_line_runs(
    const mjb_bidi_paragraph *paragraph,
    const size_t *visual_order,
    size_t count,
    mjb_bidi_run *runs,
    size_t *run_count
);

Example

mjb_bidi_paragraph paragraph;
size_t visual_order[3];
size_t run_count = 0;

if(mjb_bidi_resolve("abc", 3, MJB_ENC_UTF_8, MJB_DIRECTION_LTR,
    &paragraph) != MJB_STATUS_OK ||
    mjb_bidi_reorder_line(&paragraph, 0, 3, visual_order) != MJB_STATUS_OK ||
    mjb_bidi_line_runs(&paragraph, visual_order, 3, NULL,
        &run_count) != MJB_STATUS_OK) {
    return 1;
}

// Visual runs: 1
printf("Visual runs: %zu", run_count);
mjb_bidi_free(&paragraph);

Related

Specifications

mjb_bidi_free

Free a bidi paragraph allocated by mjb_bidi_resolve.

void mjb_bidi_free(
    mjb_bidi_paragraph *paragraph
);

Example

mjb_bidi_paragraph paragraph;

if(mjb_bidi_resolve("abc", 3, MJB_ENC_UTF_8, MJB_DIRECTION_LTR,
    &paragraph) != MJB_STATUS_OK) {
    return 1;
}

mjb_bidi_free(&paragraph);

// Paragraph released: yes
printf("Paragraph released: %s", paragraph.chars == NULL ? "yes" : "no");

Related

API section

Emoji

Inspect emoji sequences and properties.

9 functions

mjb_codepoint_emoji

Return the emoji properties.

mjb_status mjb_codepoint_emoji(
    mjb_codepoint codepoint,
    mjb_emoji_properties *emoji
);

Example

mjb_emoji_properties emoji;

if(mjb_codepoint_emoji(0x1F600, &emoji) != MJB_STATUS_OK) {
    return 1;
}

// U+1F600 has Emoji_Presentation: yes
printf("U+1F600 has Emoji_Presentation: %s", emoji.presentation ? "yes" : "no");

Related

Specifications

mjb_codepoint_is_emoji

Return true if the codepoint has the Unicode Emoji property.

bool mjb_codepoint_is_emoji(
    mjb_codepoint codepoint
);

Example

// Number sign has the Emoji property: yes
bool emoji = mjb_codepoint_is_emoji('#');

// Number sign has the Emoji property: yes
printf("Number sign has the Emoji property: %s", emoji ? "yes" : "no");

Specifications

mjb_codepoint_is_emoji_presentation

Return true if the codepoint has the Unicode Emoji_Presentation property.

bool mjb_codepoint_is_emoji_presentation(
    mjb_codepoint codepoint
);

Example

// Grinning face defaults to emoji presentation: yes
bool presentation = mjb_codepoint_is_emoji_presentation(0x1F600);

// Grinning face defaults to emoji presentation: yes
printf("Grinning face defaults to emoji presentation: %s", presentation ? "yes" : "no");

Specifications

mjb_codepoint_is_emoji_modifier

Return true if the codepoint has the Unicode Emoji_Modifier property.

bool mjb_codepoint_is_emoji_modifier(
    mjb_codepoint codepoint
);

Example

// Medium skin tone is an emoji modifier: yes
bool modifier = mjb_codepoint_is_emoji_modifier(0x1F3FD);

// Medium skin tone is an emoji modifier: yes
printf("Medium skin tone is an emoji modifier: %s", modifier ? "yes" : "no");

Specifications

mjb_codepoint_is_emoji_modifier_base

Return true if the codepoint has the Unicode Emoji_Modifier_Base property.

bool mjb_codepoint_is_emoji_modifier_base(
    mjb_codepoint codepoint
);

Example

// Waving hand accepts an emoji modifier: yes
bool modifier_base = mjb_codepoint_is_emoji_modifier_base(0x1F44B);

// Waving hand accepts an emoji modifier: yes
printf("Waving hand accepts an emoji modifier: %s", modifier_base ? "yes" : "no");

Specifications

mjb_codepoint_is_emoji_component

Return true if the codepoint has the Unicode Emoji_Component property.

bool mjb_codepoint_is_emoji_component(
    mjb_codepoint codepoint
);

Example

// Zero-width joiner is an emoji component: yes
bool component = mjb_codepoint_is_emoji_component(0x200D);

// Zero-width joiner is an emoji component: yes
printf("Zero-width joiner is an emoji component: %s", component ? "yes" : "no");

Specifications

mjb_string_emoji_sequence

Return emoji sequence metadata for a complete string.

mjb_status mjb_string_emoji_sequence(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_emoji_sequence *emoji
);

Example

const char *flag = "\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9"; // 🇮🇹
mjb_emoji_sequence emoji;

if(mjb_string_emoji_sequence(flag, strlen(flag), MJB_ENC_UTF_8,
    &emoji) != MJB_STATUS_OK) {
    return 1;
}

// Sequence codepoints: 2
printf("Sequence codepoints: %zu", emoji.codepoint_count);

Related

Specifications

mjb_string_is_emoji_sequence

Return true if the complete string is an emoji sequence listed by Unicode, including standardized emoji variation sequences.

bool mjb_string_is_emoji_sequence(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding
);

Example

const char *keycap = "1\xEF\xB8\x8F\xE2\x83\xA3"; // 1️⃣

bool listed = mjb_string_is_emoji_sequence(keycap, strlen(keycap), MJB_ENC_UTF_8);

// Listed emoji sequence: yes
printf("Listed emoji sequence: %s", listed ? "yes" : "no");

Related

Specifications

mjb_string_is_rgi_emoji

Return true if the complete string is an RGI emoji sequence, excluding plain standardized variation sequences.

bool mjb_string_is_rgi_emoji(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding
);

Example

const char *flag = "\xF0\x9F\x87\xAE\xF0\x9F\x87\xB9"; // 🇮🇹

bool rgi = mjb_string_is_rgi_emoji(flag, strlen(flag), MJB_ENC_UTF_8);

// RGI emoji: yes
printf("RGI emoji: %s", rgi ? "yes" : "no");

Related

Specifications

API section

Display width

Compute the number of display columns a string occupies in a terminal.

3 functions

mjb_truncate_width

Return the number of bytes whose grapheme clusters fit within max_columns display columns.

size_t mjb_truncate_width(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_width_context context,
    size_t max_columns
);

Example

const char *input = "A\xE7\x95\x8C"; // A界
size_t bytes = mjb_truncate_width(input, strlen(input), MJB_ENC_UTF_8,
    MJB_WIDTH_CONTEXT_WESTERN, 2);

// Two columns include 1 byte
printf("Two columns include %zu byte", bytes);

mjb_codepoint_east_asian_width

Return the east asian width of a codepoint.

mjb_status mjb_codepoint_east_asian_width(
    mjb_codepoint codepoint,
    mjb_east_asian_width *width
);

Example

mjb_east_asian_width width;

if(mjb_codepoint_east_asian_width(0x754C, &width) != MJB_STATUS_OK) { // 界
    return 1;
}

// U+754C is wide: yes
printf("U+754C is wide: %s", width == MJB_EAW_WIDE ? "yes" : "no");

Related

Specifications

mjb_display_width

Return the display width of a string.

mjb_status mjb_display_width(
    const char *buffer,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_width_context context,
    size_t *width
);

Compute the number of display columns a string occupies in a terminal, accounting for wide and ambiguous East Asian characters, combining marks, and emoji sequences.

Returns

  • MJB_STATUS_OK — The width was computed
  • MJB_STATUS_INVALID_ARGUMENTwidth is NULL, or buffer is NULL with a non-zero size
  • MJB_STATUS_OVERFLOW — The width would overflow

Example

const char *input = "A\xE7\x95\x8C"; // A界
size_t width;

if(mjb_display_width(input, strlen(input), MJB_ENC_UTF_8,
    MJB_WIDTH_CONTEXT_WESTERN, &width) != MJB_STATUS_OK) {
    return 1;
}

// Display columns: 3
printf("Display columns: %zu", width);

Related

Specifications

API section

Hangul language

Inspect Hangul syllables and perform Hangul syllable decomposition and composition.

8 functions

mjb_codepoint_is_hangul_l

Return if the codepoint is a hangul L.

bool mjb_codepoint_is_hangul_l(
    mjb_codepoint codepoint
);

Example

// U+1100 is a leading Jamo: yes
printf("U+1100 is a leading Jamo: %s", mjb_codepoint_is_hangul_l(0x1100) ? "yes" : "no");

mjb_codepoint_is_hangul_v

Return if the codepoint is a hangul V.

bool mjb_codepoint_is_hangul_v(
    mjb_codepoint codepoint
);

Example

// U+1161 is a vowel Jamo: yes
printf("U+1161 is a vowel Jamo: %s", mjb_codepoint_is_hangul_v(0x1161) ? "yes" : "no");

mjb_codepoint_is_hangul_t

Return if the codepoint is a hangul T.

bool mjb_codepoint_is_hangul_t(
    mjb_codepoint codepoint
);

Example

// U+11A8 is a trailing Jamo: yes
printf("U+11A8 is a trailing Jamo: %s", mjb_codepoint_is_hangul_t(0x11A8) ? "yes" : "no");

mjb_codepoint_is_hangul_jamo

Return if the codepoint is a hangul jamo.

bool mjb_codepoint_is_hangul_jamo(
    mjb_codepoint codepoint
);

Example

// U+1100 is Hangul Jamo: yes
printf("U+1100 is Hangul Jamo: %s", mjb_codepoint_is_hangul_jamo(0x1100) ? "yes" : "no");

mjb_codepoint_is_hangul_syllable

Return if the codepoint is a hangul syllable.

bool mjb_codepoint_is_hangul_syllable(
    mjb_codepoint codepoint
);

Example

// U+AC00 is a Hangul syllable: yes
printf("U+AC00 is a Hangul syllable: %s", mjb_codepoint_is_hangul_syllable(0xAC00) ? "yes" : "no");

mjb_hangul_syllable_name

Return hangul syllable name.

mjb_status mjb_hangul_syllable_name(
    mjb_codepoint codepoint,
    char *buffer,
    size_t byte_length
);

Example

char name[32];

if(mjb_hangul_syllable_name(0xAC01, name, sizeof(name)) != MJB_STATUS_OK) {
    return 1;
}

// Name: HANGUL SYLLABLE GAG
printf("Name: %s", name);

mjb_hangul_syllable_decomposition

Hangul syllable decomposition.

mjb_status mjb_hangul_syllable_decomposition(
    mjb_codepoint codepoint,
    mjb_codepoint *codepoints
);

Example

mjb_codepoint decomposition[3];

if(mjb_hangul_syllable_decomposition(0xAC01,
    decomposition) != MJB_STATUS_OK) {
    return 1;
}

// Decomposition starts with: U+1100
printf("Decomposition starts with: U+%04X", decomposition[0]);

mjb_hangul_syllable_composition

Hangul syllable composition.

size_t mjb_hangul_syllable_composition(
    mjb_buffer_character *characters,
    size_t characters_len
);

Example

mjb_buffer_character characters[] = {
    { 0x1100, 0 }, // choseong kiyeok
    { 0x1161, 0 }, // jungseong a
    { 0x11A8, 0 }  // jongseong kiyeok
};
size_t length = mjb_hangul_syllable_composition(characters, 3);

// Composition: U+AC01
printf("Composition: U+%04X", length == 1 ? characters[0].codepoint : 0);

API section

Utilities

Use locales, memory hooks, version details, and low-level string helpers.

12 functions

mjb_property_name

Return the name of a property, NULL if the property specified is not valid.

const char *mjb_property_name(
    mjb_property property
);

Example

const char *name = mjb_property_name(MJB_PR_ALPHABETIC);

// Property: Alphabetic
printf("Property: %s", name);

Specifications

mjb_locale_parse

Parse a BCP 47 language tag.

mjb_status mjb_locale_parse(
    const char *id,
    size_t byte_length,
    mjb_encoding encoding,
    mjb_locale_id *locale,
    mjb_error *error
);

Parse a BCP 47 language tag, such as sr-Latn-RS, into its components: language, extended language, script, region, variant, extensions, private use, and grandfathered tags. Parsing is strict: malformed tags are rejected and error is filled with the failure reason.

Returns

  • MJB_STATUS_OK — The tag was parsed and locale filled
  • MJB_STATUS_INVALID_ARGUMENT — An argument is NULL or the tag is not a valid BCP 47 language tag
  • MJB_STATUS_NO_MEMORY — Allocation failed

Example

mjb_locale_id locale;
mjb_error error;

if(mjb_locale_parse("sr-Latn-RS", 10, MJB_ENC_UTF_8, &locale,
    &error) != MJB_STATUS_OK) {
    return 1;
}

// Locale: sr Latn RS
printf("Locale: %s %s %s", locale.language, locale.script, locale.region);

Related

Specifications

mjb_locale_set

Set current locale used by locale-sensitive casing.

mjb_status mjb_locale_set(
    unsigned int locale
);

Set the process-global locale used by mjb_case. The default locale is MJB_LOCALE_EN, and mjb_shutdown resets it to MJB_LOCALE_EN. Only MJB_LOCALE_TR, MJB_LOCALE_AZ, and MJB_LOCALE_LT currently tailor casing. Other valid locale values are accepted but do not change Unicode algorithm behavior.

Returns

  • MJB_STATUS_OK — The locale was set
  • MJB_STATUS_INVALID_ARGUMENTlocale is not a valid mjb_locale value

Example

if(mjb_locale_set(MJB_LOCALE_TR) != MJB_STATUS_OK) {
    return 1;
}

// Turkish locale selected: yes
printf("Turkish locale selected: yes");
if(mjb_locale_set(MJB_LOCALE_EN) != MJB_STATUS_OK) {
    return 1;
}

Related

mjb_result_free

Free a mjb_result.

mjb_status mjb_result_free(
    mjb_result *result
);

Free the memory allocated for a mjb_result. The result pointer is set to NULL.

Returns

  • MJB_STATUS_OK — The result was freed
  • MJB_STATUS_INVALID_ARGUMENTresult is NULL

Example

mjb_result result;

if(mjb_string_convert_encoding("A", 1, MJB_ENC_UTF_8, MJB_ENC_UTF_16LE,
    &result) != MJB_STATUS_OK || mjb_result_free(&result) != MJB_STATUS_OK) {
    return 1;
}

// Result released: yes
printf("Result released: %s", result.output == NULL ? "yes" : "no");

mjb_version

Output the current library version (MJB_VERSION).

const char *mjb_version(void);

Output the current library version as a string, such as "1.0.0".

Example

const char *version = mjb_version();

// Version is available: yes
printf("Version is available: %s", version[0] != '\0' ? "yes" : "no");

Related

mjb_version_number

Output the current library version number (MJB_VERSION_NUMBER).

unsigned int mjb_version_number(void);

Output the current library version number as an unsigned integer.

Example

unsigned int version = mjb_version_number();

// Version number is positive: yes
printf("Version number is positive: %s", version > 0 ? "yes" : "no");

Related

mjb_unicode_version

Output the current supported Unicode version (MJB_UNICODE_VERSION).

const char *mjb_unicode_version(void);

Output the current supported Unicode version as a string, such as "15.0.0".

Example

const char *version = mjb_unicode_version();

// Unicode version: 17.0.0
printf("Unicode version: %s", version);

Related

mjb_set_memory_functions

Set the library memory functions.

mjb_status mjb_set_memory_functions(
    mjb_alloc_fn alloc_fn,
    mjb_realloc_fn realloc_fn,
    mjb_free_fn free_fn
);

Replace the allocator used by the library for all internal allocations and for the buffers returned in mjb_result. Must be called before any other library call.

Example

mjb_shutdown(); // Ensure no allocator is currently locked in.

if(mjb_set_memory_functions(malloc, realloc, free) != MJB_STATUS_OK) {
    return 1;
}

// Standard allocator installed: yes
printf("Standard allocator installed: yes");
mjb_shutdown();

Related

mjb_shutdown

Shutdown the library. Not needed to be called.

void mjb_shutdown(void);

Example

mjb_shutdown();

// Library state reset: yes
printf("Library state reset: yes");

mjb_alloc

Allocate memory.

void *mjb_alloc(
    size_t byte_length
);

Allocate memory using the allocator set by mjb_set_memory_functions. If no allocator is set, the default allocator is used.

Example

char *buffer = (char*)mjb_alloc(32);

if(buffer == NULL) {
    return 1;
}

strcpy(buffer, "allocated");

// Buffer: allocated
printf("Buffer: %s", buffer);
mjb_free(buffer);

Related

mjb_realloc

Reallocate memory.

void *mjb_realloc(
    void *ptr,
    size_t new_size
);

Reallocate memory using the allocator set by mjb_set_memory_functions. If no allocator is set, the default allocator is used.

Example

char *buffer = (char*)mjb_alloc(8);

if(buffer == NULL) {
    return 1;
}

char *larger = (char*)mjb_realloc(buffer, 32);

if(larger == NULL) {
    mjb_free(buffer);
    return 1;
}

// Reallocation succeeded: yes
printf("Reallocation succeeded: yes");
mjb_free(larger);

Related

mjb_free

Free memory.

void mjb_free(
    void *ptr
);

Free memory using the allocator set by mjb_set_memory_functions. If no allocator is set, the default allocator is used.

Example

void *memory = mjb_alloc(16);

if(memory == NULL) {
    return 1;
}

mjb_free(memory);

// Memory freed: yes
printf("Memory freed: yes");

Related