Preview of main at d0a501a, 2026-11-07

idna is a header-only library of internationalized domain names in C++20, one of the libraries of webcpp: Punycode (RFC 3492), Unicode’s UTS 46 processing, to ASCII and to Unicode, and the validity rules of IDNA2008 (RFC 5890 to 5893), over Unicode 17.0.0 tables generated into its headers. Every operation returns its error as a value, and every operation that writes into a caller’s buffer allocates nothing.

This page teaches idna concept by concept: why each exists, its smallest example, its variations and its rules. Every program it shows was compiled and run, natively and for wasm32-wasip2, wasm32-wasip3 and Emscripten, and printed the output shown under it. The reference that closes it lists every name the headers declare.

Introduction

The labels of a domain name were ASCII: letters, digits and the hyphen. An internationalized domain name lets a label hold any script, bücher.example or παράδειγμα.δοκιμή, and still reach the DNS, which stays ASCII: a label that is not ASCII travels as an A-label, xn-- followed by the label’s code points in Punycode, so bücher.example is looked up as xn--bcher-kva.example.

Two standards say which labels are valid, and how a name a person typed becomes one:

  • IDNA2008, RFC 5890 to 5893, defines a valid label: the property of each code point, derived from Unicode (PVALID; CONTEXTJ and CONTEXTO, valid in a context the RFC states; DISALLOWED), Normalization Form C, the hyphens, and the Bidi rule of a name that mixes right-to-left and left-to-right labels. It maps nothing: bücher is a valid label, and Bücher is not.

  • UTS 46, Unicode’s IDNA Compatibility Processing, first maps what was typed (case, compatibility forms, the full stops of other scripts), then normalizes it, then checks it, by rules close to IDNA2008’s but not the same: it accepts symbols such as ¡, which IDNA2008 disallows, and has no CONTEXTO rules at all.

idna implements both, and its default profile is the stricter union of the two: UTS 46’s mapping, then IDNA2008’s validity on top of UTS 46’s. It is what JSON Schema’s idn-hostname format asks for, and what no UTS 46 library measured against that format’s tests gives alone: each accepts the labels IDNA2008 refuses. idna::options{.idna2008 = false} is UTS 46 alone.

A first example

This program converts two names to their ASCII form, and an A-label back to its Unicode form, with the default options:

#include <webcpp/idna.hpp>

#include <array>
#include <iostream>
#include <string_view>

namespace idna = webcpp::idna;

int main() {
    std::array<char, idna::max_domain_length> ascii{};
    for (const std::string_view name : {"B\303\274cher.Example", "a\302\267b.example"}) {
        const idna::result<std::size_t> converted = idna::to_ascii(ascii, name);
        if (converted.has_error()) {
            std::cout << name << " -> error: " << converted.error().message() << '\n';
            continue;
        }
        std::cout << name << " -> " << std::string_view(ascii.data(), converted.value()) << '\n';
    }

    std::array<char, 1024> unicode{};
    const idna::unicode_result shown = idna::to_unicode(unicode, "xn--bcher-kva.example");
    std::cout << std::string_view(unicode.data(), shown.size)
              << (shown.error ? " (with an error)" : "") << '\n';
}

It prints:

Bücher.Example -> xn--bcher-kva.example
a·b.example -> error: code point outside its allowed context
bücher.example

Bücher.Example is mapped first, its capitals lowered, so it is valid, and its first label, which is not ASCII, becomes an A-label. a·b.example is refused: U+00B7 MIDDLE DOT is valid in IDNA2008 only between two l, a CONTEXTO rule that UTS 46 alone does not have. to_unicode decodes the A-label back, and reports no error.

Headers

<webcpp/idna.hpp> includes every public header; the names are in the namespace webcpp::idna, and Punycode’s in webcpp::idna::punycode:

Header What it declares

<webcpp/idna/errors.hpp>

idna::error, its category, and idna::result.

<webcpp/idna/options.hpp>

idna::options, the DNS lengths, and the version of Unicode.

<webcpp/idna/punycode.hpp>

punycode::encode and punycode::decode (Punycode).

<webcpp/idna/validity.hpp>

check_label, check_domain and their is_valid_ forms (Validity).

<webcpp/idna/uts46.hpp>

to_ascii and to_unicode (Converting names).

Requirements

  • A C++20 compiler. idna’s tests and examples are built and run on every native lane of webcpp’s continuous integration, for wasm32-wasip2 and wasm32-wasip3 with the Clang of wasi-sdk, and with Emscripten under Node.

  • Boost’s headers, 1.92 or newer: Config, System, Core and Assert. Nothing of Boost is built or linked for idna.

  • webcpp’s configuration, config, whose <webcpp/config.hpp> every header of idna includes first. It is header-only too, and idna needs nothing else.

Punycode

Punycode, RFC 3492, writes a string of code points with the ASCII letters, digits and hyphen alone, and reads it back: the code points below U+0080, the basic ones, are copied first, then a hyphen, then numbers in base 36 that say where each other code point goes. An A-label is xn-- followed by the Punycode of its U-label. idna::punycode::encode and decode are that encoding, without the prefix, which the conversions add and remove. Here is RFC 3492 section 7.1’s sample (L), 3年B組金八先生:

// RFC 3492 section 7.1's sample (L).
const std::u32string_view sample = U"3\u5E74B\u7D44\u91D1\u516B\u5148\u751F";
std::array<char, 64> encoded{};
const idna::result<std::size_t> written = idna::punycode::encode(encoded, sample);
std::cout << std::string_view(encoded.data(), written.value()) << '\n';

decode gives the code points back, into a span of char32_t:

std::array<char32_t, 64> decoded{};
const idna::result<std::size_t> read =
    idna::punycode::decode(decoded, "3B-ww4c5e180e575a65lsy2b");
std::cout << read.value() << " code points, the same: "
          << (std::u32string_view(decoded.data(), read.value()) == sample ? "yes" : "no")
          << '\n';

Both return the number of chars or code points written, or an error: a character that is no digit, a number cut short, a value that is not a Unicode scalar value, or arithmetic past 32 bits, which RFC 3492 section 6.4 refuses:

for (const std::string_view bad : {"-9uc", "bcher-k", "999999999999999999999"}) {
    std::cout << bad << " -> " << idna::punycode::decode(decoded, bad).error().message()
              << '\n';
}

The program prints:

3B-ww4c5e180e575a65lsy2b
8 code points, the same: yes
-9uc -> invalid Punycode
bcher-k -> invalid Punycode
999999999999999999999 -> Punycode overflow

The digits are read in either case; the basic code points are copied as they are, case included, as RFC 3492’s mixed-case annotation leaves them, and encode writes its digits in lowercase. A hyphen at the very start is no delimiter, since nothing precedes it, so -9uc is refused, as RFC 3492 says: it is the A-label xn---9uc, which a careless decoder would read as xn--9uc. Each function has a second form that returns a std::pmr string from a memory resource (Allocation). Neither form bounds the length of its input: a pmr form’s time grows with the square of it, and a span form’s at most with the input’s length times the span’s size, so a program bounds input it does not trust before it hands it to a pmr form (Punycode’s bound).

Converting names

idna::to_ascii and idna::to_unicode are UTS 46’s ToASCII and ToUnicode, sections 4.2 and 4.3: the name is mapped, normalized to NFC, split into labels at each full stop, and each label is converted and checked. The first example (A first example) calls both. Each has two forms: one that writes into the caller’s span and allocates nothing, and one that returns a string from a memory resource (Allocation).

The processing

Each code point is looked up in UTS 46’s mapping table: a code point that is valid stays, one that is mapped is replaced by its mapping (A by a, fi by fi, U+3002 IDEOGRAPHIC FULL STOP by .), one that is ignored is removed (U+00AD SOFT HYPHEN), a deviation stays unless the processing is transitional (ß, ς, and the two joiners), and one that is disallowed stays, to be refused when its label is checked. The result is normalized to NFC and split at U+002E. A label that starts with xn-- is decoded from Punycode and checked as the U-label it holds; any other is checked as it is. The checks, in their order, are those of The rules.

idna does this one label at a time, in a working storage of one label’s code points, so a name of any length needs the same small storage (Limits).

To ASCII

to_ascii(output, domain, opts) writes the ASCII form of domain into the caller’s span and returns the number of chars written: each label that is not ASCII as xn-- and its Punycode, the others as they are, joined by .. A name that fails any check has no ASCII form: the result is the first error found, in the order of the labels, then bidi for the Bidi rule across the labels, then domain_too_long, and output_too_small last, so a span too small for a name that is too long anyway reports the name. With the DNS lengths verified, the default, the ASCII form is at most idna::max_domain_length, 253 chars, so a std::array<char, idna::max_domain_length> always holds it.

to_ascii(domain, opts, resource) returns the same ASCII form as a std::pmr::string. With the DNS lengths verified, it reports the same first error as a span form large enough to hold the result, for every name, and it is never output_too_small: a label that outgrows the span form’s working storage is too long for the DNS anyway, and both forms refuse it there (The working storage).

To Unicode

to_unicode(output, domain, opts) writes the Unicode form, in UTF-8, and returns an idna::unicode_result: how many chars it wrote, and the first error. As UTS 46 asks, the conversion goes on after an error, so that a name can be shown even when it is not valid: each label is written as the processing leaves it, mapped and normalized, an A-label decoded when its Punycode decodes and kept as it is otherwise. A label that fails its checks is written so too, as the toUnicode column of IdnaTestV2 records it, and no U+FFFD is put in place of what failed: A_B.Example is written a_b.example, with not_ldh. With the DNS lengths verified, a final empty label, the DNS root of example., is no error in the Unicode form, as UTS 46’s X4_2 allows; any other empty label is empty_label. When the span is full, the writing stops at a code point boundary and the error is output_too_small, which takes the place of any other: the caller needs a larger span before anything else. Input that is not well-formed UTF-8 writes nothing, and is invalid_utf8.

to_unicode(domain, opts, resource) returns an idna::unicode_string, the Unicode form as a std::pmr::string and the first error. It is never output_too_small, and the two forms differ only past the working storage, which the Unicode form, checking no label’s maximum length, meets with the default options: the span form stops at a label beyond it, and the pmr form converts the label (The working storage).

Options

Every conversion and every check takes an idna::options, the flags of UTS 46 section 4 and one of idna’s own, idna2008. The defaults are the strict profile: every check on, nontransitional processing, the DNS lengths verified, and IDNA2008’s validity on top. idna::options is an aggregate, so a program names only what changes: idna::options{.idna2008 = false} is UTS 46 alone, and idna::options{.verify_dns_length = false} lets labels of any length through.

Member UTS 46’s flag Default When true

check_hyphens

CheckHyphens

true

No hyphen in both the third and the fourth position of a label (V2), none at either end (V3). When false, only a label that starts with xn-- is refused (V4).

check_bidi

CheckBidi

true

Every label of a Bidi domain name, one that holds an R, AL or AN code point, satisfies the six rules of RFC 5893 section 2.

check_joiners

CheckJoiners

true

U+200C ZERO WIDTH NON-JOINER and U+200D ZERO WIDTH JOINER stand only where RFC 5892 A.1 and A.2 allow them.

use_std3_ascii_rules

UseSTD3ASCIIRules

true

The only ASCII code points of a label are a to z, 0 to 9 and the hyphen (U1).

verify_dns_length

VerifyDnsLength

true

Every label is 1 to 63 octets in its ASCII form, the name 1 to 253, and a final empty root label is refused (A4_1, A4_2).

transitional_processing

Transitional_Processing

false

The deviations are mapped: ß to ss, ς to σ, the joiners removed. UTS 46 deprecates it.

ignore_invalid_punycode

IgnoreInvalidPunycode

false

A label that starts with xn-- and does not decode is kept as it is, without an error.

idna2008

none

true

IDNA2008’s validity on top of UTS 46’s: a code point that UTS 46 marks NV8 or XV8, DISALLOWED in IDNA2008, is refused, and the CONTEXTO rules of RFC 5892 A.3 to A.9 apply.

Validity

idna::check_label and idna::check_domain say whether a label, or a domain name, is valid as it is given, by IDNA2008’s rules and UTS 46’s validity criteria, without UTS 46’s mapping: nothing is lowercased, normalized or replaced, so a label with a capital letter, or one not in NFC, is refused. The one exception is an A-label, whose case does not matter (below). They are the checks a program needs when a name must already be in its final form, as a hostname’s A-labels must. is_valid_label and is_valid_domain say the same as a bool.

// "\302\267" is U+00B7 MIDDLE DOT in UTF-8, which RFC 5892 allows only between two 'l'.
for (const std::string_view label :
     {"example", "xn--bcher-kva", "l\302\267l", "a\302\267l", "ab--cd", "Example"}) {
    const idna::result<void> checked = idna::check_label(label);
    std::cout << label << ": " << (checked.has_value() ? "valid" : checked.error().message())
              << '\n';
}

An A-label’s xn-- and Punycode are read in either case, as RFC 5890 compares them. The label must be ASCII, and its Punycode must decode to a label that holds a code point beyond ASCII (UTS 46’s P4). Its U-label is then checked by the rules below, Normalization Form C included, and encoded again: an A-label that is not the encoding of its U-label, its letters compared in lowercase, is refused, as RFC 5891 section 5.4 asks. Each failure of the A-label itself, a code point beyond ASCII, Punycode that does not decode, P4 or the round trip, is invalid_punycode, or punycode_overflow for a number past 32 bits. Under ignore_invalid_punycode, a label whose Punycode does not decode is kept as it is, and only its length is checked.

check_label refuses a U+002E inside its label as disallowed, and applies the Bidi rule to its label as to a name of one label. check_domain splits at U+002E alone: the other full stops, U+3002, U+FF0E and U+FF61, separate labels only after UTS 46’s mapping, so as given they are code points UTS 46 maps, and disallowed. It applies the Bidi rule across the labels, and adds the length of the whole name:

// "\327\220" is U+05D0 HEBREW LETTER ALEF in UTF-8, a right-to-left letter.
for (const std::string_view domain : {"a.\327\220", "0a.\327\220", "0a.b"}) {
    std::cout << domain << ": " << (idna::is_valid_domain(domain) ? "valid" : "invalid")
              << '\n';
}

The same check under UTS 46 alone accepts what IDNA2008’s context rules refuse:

const idna::options uts46_alone{.idna2008 = false};
std::cout << "a\302\267l under UTS 46 alone: "
          << (idna::is_valid_label("a\302\267l", uts46_alone) ? "valid" : "invalid") << '\n';

The program prints:

example: valid
xn--bcher-kva: valid
l·l: valid
a·l: code point outside its allowed context
ab--cd: hyphens in the third and fourth positions
Example: disallowed code point
a.א: valid
0a.א: invalid
0a.b: valid
a·l under UTS 46 alone: valid

The rules

A label is checked in this order, and the first rule it breaks is its error; to_ascii and to_unicode apply the same rules to each label after the mapping, with the NFC check only for a decoded A-label:

  1. Normalization Form C (UTS 46 V1, RFC 5891 section 5.4): not_nfc.

  2. Hyphens, under check_hyphens: none in both the third and the fourth position (V2), hyphen_in_third_and_fourth, which keeps such labels free for a future prefix, xn-- among them; none at either end (V3), hyphen_at_start_or_end. Without check_hyphens, a label that starts with xn-- (V4), ace_prefix.

  3. No leading combining mark (V6, RFC 5891 section 5.4): leading_combining_mark.

  4. UTS 46’s status (V7): every code point valid, or a deviation in nontransitional processing; disallowed.

  5. STD3 ASCII, under use_std3_ascii_rules (U1): ASCII only a to z, 0 to 9 and the hyphen; not_ldh.

  6. CONTEXTJ, under check_joiners (RFC 5892 A.1, A.2): U+200C after a virama, or between a code point of Joining_Type L or D and one of R or D, with only T between; U+200D after a virama; contextj.

  7. IDNA2008, under idna2008: no code point UTS 46 marks NV8 or XV8, not_idna2008; then the CONTEXTO rules, contexto: U+00B7 MIDDLE DOT between two l (A.3), U+0375 GREEK KERAIA before a Greek code point (A.4), U+05F3 and U+05F4, GERESH and GERSHAYIM, after a Hebrew one (A.5, A.6), U+30FB KATAKANA MIDDLE DOT in a label that holds Hiragana, Katakana or Han (A.7), and Arabic-Indic digits never beside extended ones (A.8, A.9).

Across the labels of a name, under check_bidi: when one label holds an R, AL or AN code point, the name is a Bidi domain name, and then every label must satisfy RFC 5893 section 2, bidi. 0a.א fails: its first label starts with a digit, which rule 1 refuses in a Bidi domain name. Under verify_dns_length, the lengths of the ASCII form: each label’s with the label, an empty one as empty_label and, after its rules, one of more than 63 octets as label_too_long; the whole name’s last, after the Bidi rule, domain_too_long. A label that outgrows the working storage of 256 code points, which no label within the DNS lengths does, is refused as soon as it does, before its rules: label_too_long under verify_dns_length, storage_exhausted without it (Errors).

idna derives IDNA2008’s property of a code point from UTS 46’s table: PVALID, CONTEXTJ or CONTEXTO exactly when its status is valid or deviation and it bears no NV8 or XV8 mark. The generator checks this against IANA’s IDNA2008 tables each time it runs (Unicode and the tables).

Errors

Every operation of idna that can fail returns its error as a value: an idna::result<T>, Boost.System’s result, or, for to_unicode, which always converts what it can, a struct whose error is a boost::system::error_code. idna throws nothing of its own. Its errors are the values of idna::error, of the category webcpp.idna, one per family of rules rather than one per status code of UTS 46: IdnaTestV2, Unicode’s own conformance file, says an implementation "need only record that there is an error". The first error found is the one reported, but for output_too_small: to_ascii reports it last, after every label’s error, bidi and domain_too_long, and the span form of to_unicode lets it replace an earlier error (Converting names). The labels are processed in order, and the checks of a label in the order UTS 46 section 4.1 lists them, IDNA2008’s last.

Value Enumerator Reports Message

1

invalid_utf8

input that is not well-formed UTF-8 (RFC 3629)

invalid UTF-8

2

disallowed

UTS 46 V7: a status other than valid, or deviation when nontransitional; V5: a U+002E inside one label

disallowed code point

3

not_idna2008

a code point UTS 46 marks NV8 or XV8, DISALLOWED in IDNA2008

code point disallowed by IDNA2008

4

not_ldh

UTS 46 U1: ASCII other than a to z, 0 to 9 and the hyphen

ASCII code point other than a letter, a digit or a hyphen

5

not_nfc

UTS 46 V1, RFC 5891 section 5.4

label not in Normalization Form C

6

hyphen_in_third_and_fourth

UTS 46 V2

hyphens in the third and fourth positions

7

hyphen_at_start_or_end

UTS 46 V3

hyphen at the start or end of a label

8

ace_prefix

UTS 46 V4: xn-- while check_hyphens is off

label starts with xn--

9

leading_combining_mark

UTS 46 V6, RFC 5891 section 5.4

label starts with a combining mark

10

contextj

IdnaTestV2 C1, C2: RFC 5892 A.1, A.2

joiner outside its allowed context

11

contexto

RFC 5892 A.3 to A.9

code point outside its allowed context

12

bidi

IdnaTestV2 B1 to B6: RFC 5893 section 2

Bidi rule violated

13

invalid_punycode

RFC 3492 decoding; UTS 46 P4; an A-label not in its canonical form

invalid Punycode

14

punycode_overflow

RFC 3492 section 6.4; UTS 46 A3; a label’s Punycode past the conversions’ bound of 256 code points (Punycode’s bound)

Punycode overflow

15

empty_label

UTS 46 A4_2 and X4_2

empty label

16

label_too_long

UTS 46 A4_2: more than 63 octets

label longer than 63 octets

17

domain_too_long

UTS 46 A4_1: more than 253 octets

domain name longer than 253 octets

18

output_too_small

the caller’s span cannot hold the result

output too small

19

storage_exhausted

a label beyond the inline working storage, with no memory resource

label beyond the working storage

The values are fixed: none changes, and none is reused for another meaning. An idna::error converts to an error_code and compares with one, result.error() == idna::error::contexto.

Allocation

idna allocates nothing it does not have to, and lets its user choose where what it does allocate comes from. Every conversion has two forms:

  • A span form writes into the caller’s buffer, std::span<char> or std::span<char32_t>, and allocates nothing, whatever its input. The validators, check_label and check_domain, allocate nothing either.

  • A pmr form returns a std::pmr::string or std::pmr::u32string from the std::pmr::memory_resource it is given, by default std::pmr::get_default_resource(). It allocates from that resource alone: its result, and a label’s working storage beyond the inline capacity (Limits).

The span form of to_ascii needs no more than a buffer on the stack:

// A span form writes into the caller's buffer and allocates nothing.
std::array<char, idna::max_domain_length> buffer{};
const idna::result<std::size_t> written = idna::to_ascii(buffer, "b\303\274cher.example");
std::cout << std::string_view(buffer.data(), written.value()) << '\n';

Its pmr form, here with a std::pmr::monotonic_buffer_resource over a buffer of the program’s own, which allocates nothing from the heap either:

// A pmr form takes its memory from the resource it is given, here a buffer on the stack.
std::array<std::byte, 16384> storage{};
std::pmr::monotonic_buffer_resource resource(storage.data(), storage.size(),
                                             std::pmr::null_memory_resource());
const idna::result<std::pmr::string> owned =
    idna::to_ascii("b\303\274cher.example", idna::options(), resource);
std::cout << owned.value() << '\n';

The two forms of to_ascii differ for a label longer than the inline storage, which only a program that turns the DNS lengths off can give it: the span form refuses it, and the pmr form holds it in its resource. to_unicode, which checks no label’s maximum length, meets such a label with the default options too (The working storage).

// Without the DNS lengths, a label of 300 code points is beyond the inline storage: the span
// form refuses it, and the pmr form holds it in its resource.
const std::string long_label(300, 'a');
const idna::options no_lengths{.verify_dns_length = false};
std::array<char, 512> wide{};
std::cout << idna::to_ascii(wide, long_label, no_lengths).error().message() << '\n';
std::cout << idna::to_ascii(long_label, no_lengths, resource).value().size() << " octets\n";

The program prints:

xn--bcher-kva.example
xn--bcher-kva.example
label beyond the working storage
300 octets

The test allocation replaces the global operator new for itself alone and counts: no call in any span form or validator, over long names, long labels and every kind of failure, with the strict profile, UTS 46 alone and the DNS lengths off, and in the pmr forms, exactly the calls of their memory resource (guarantee 1).

Limits

The DNS lengths

Under verify_dns_length, the default, a label is 1 to idna::max_label_length, 63, octets in its ASCII form, and a name 1 to idna::max_domain_length, 253, without a final root label, which is refused: RFC 1034 section 3.1’s limits, as UTS 46 section 4.2 states them. A U-label is measured by its A-label: 55 a and a ü make an A-label of 63 octets, and 56 make one of 64.

The working storage

The conversions and the checks hold one label at a time, in a working storage of 256 code points inside the call, about 1.3 KiB with the label’s ASCII form, and a second one while a label is checked for NFC the slow way. Every label the DNS lengths accept fits: an A-label decodes to at most 59 code points, and a code point decomposes to at most 4, U+1FAF among them, the longest canonical decomposition of Unicode 17.0.0, which the generator checks.

A label beyond 256 code points once decomposed is too long for the DNS, and what meets it depends on the length check:

  • Under verify_dns_length, to_ascii, in both forms, and the validators refuse it as label_too_long, which it is, as they refuse a label whose NFC check’s copy outgrows the storage.

  • Without it, the span form of to_ascii and the validators return storage_exhausted, and the pmr form of to_ascii takes the label’s storage from its memory resource.

  • to_unicode checks no label’s or name’s maximum length, and meets such a label with any options: its span form stops there with storage_exhausted, and its pmr form converts the label, an A-label within Punycode’s bound (below). The difference is by design: a span form allocates nothing, and the label is no name the DNS can hold.

The storage fills while a label is gathered, before any of the label’s checks, so a label beyond it reports that first: U+FFFF followed by 300 a is label_too_long, not disallowed. In the span form of to_unicode, the processing stops there, keeping an error recorded before it, and its output ends with the last label written before the long one: a name of the labels a_b, 300 a and c gives a_b, and not_ldh.

Punycode’s bound

Punycode’s work grows with the square of a label’s length: RFC 3492’s encoder passes over the label once for each distinct code point, and its decoder inserts each code point among those before it. The conversions bound it at 256 code points, the working storage’s, and Punycode past the bound is punycode_overflow whatever ignore_invalid_punycode says, since a limit of the implementation is no invalid Punycode. to_ascii encodes, so a label of more than 256 code points that is not ASCII is punycode_overflow there. Both conversions decode, so an A-label whose Punycode decodes to more than 256 code points is punycode_overflow in either, and kept as it is in the Unicode form. to_unicode encodes nothing: a long label that is not an A-label is written as it is. Only a pmr form meets the bound, to_ascii without the DNS lengths, and to_unicode, with any options, for an A-label, since a span form refuses such a label as beyond its storage first. At the bound, the worst label costs about 4 times one as long that Punycode handles in linear time, measured at -O2.

punycode::encode and punycode::decode are the primitive, and have no such bound: their pmr forms take input of any length, in time that grows with its square, and their span forms stop when the caller’s span is full, in time that grows at most with the input’s length times the span’s size. A program bounds the length of input it does not trust before it hands it to a pmr form.

The stack

The deepest call of idna, a check of a label, or a conversion of an A-label, whose U-label holds more than 32 combining marks in a row, which the slow NFC check sorts by counting, ran in about 11 KiB of stack, and the conversions and checks of an ordinary name, such as xn--bcher-kva.example, in about 8 KiB, a program’s start and its printf included, against the 64 KiB a WebAssembly program gets by default. These are measured figures, with no margin promised: a program built with wasi-sdk 34 in a debug build (-O0 -g) for wasm32-wasip2 and wasm32-wasip3, run by wasmtime 47 with a stack made smaller, 256 bytes at a time, until it trapped. Another compiler or version may need more. idna does not recurse.

Unicode and the tables

idna’s tables follow Unicode 17.0.0, idna::unicode_version, and UTS 46 revision 35 for it. They are generated from Unicode’s data files into <webcpp/idna/detail/tables.hpp> as constexpr arrays of constant data, never written: no table is built at run time, none is compressed, nothing is allocated and nothing is shared mutable state, so any number of threads may call idna at once. Each property is a sorted list of runs, looked up by binary search.

Table From Used by

UTS 46 status, NV8 and XV8 mark, mapping

idna/IdnaMappingTable.txt

the mapping, V7, IDNA2008’s property

Bidi_Class

ucd/extracted/DerivedBidiClass.txt

RFC 5893

Joining_Type

ucd/extracted/DerivedJoiningType.txt

RFC 5892 A.1

Canonical_Combining_Class

ucd/extracted/DerivedCombiningClass.txt

NFC, and the virama of A.1 and A.2

General_Category Mark

ucd/UnicodeData.txt

V6

Script: Greek, Hebrew, Hiragana, Katakana, Han

ucd/Scripts.txt

RFC 5892 A.4 to A.7

canonical decompositions, primary composites, NFC_Quick_Check

ucd/UnicodeData.txt, ucd/DerivedNormalizationProps.txt

NFC

Hangul syllables are decomposed and composed by the arithmetic of Unicode section 3.12, and take no table.

The generator

tools/generate_tables.py, in idna’s repository, writes the tables, and the vectors of the conformance tests, IdnaTestV2.txt and NormalizationTest.txt as C++ data under test/generated/ (Testing). Its inputs are pinned in tools/unicode.json, each by its URL under https://www.unicode.org/Public/17.0.0/ and its SHA-256, with IANA’s IDNA2008 tables, idna-tables-12.0.0, read only by its checks. A file its cache lacks is downloaded, every file is hashed, and a digest that differs stops it, naming the file and both digests, before it writes anything.

Before it writes, it checks what idna assumes of the data, and stops naming the assumption that fails: every status of the mapping table is one of the five it knows; IANA’s IDNA2008 property of every code point IANA assigns one agrees with the derivation from UTS 46’s table, U+002E FULL STOP aside, which IANA disallows and UTS 46 keeps as the separator no label holds; the CONTEXTJ and CONTEXTO code points are those of RFC 5892 appendix A; no full canonical decomposition is longer than 4 code points; no Hangul syllable has a decomposition in UnicodeData.txt, since idna computes those; and every property value a file holds is one it knows, either one the tables keep or one it reads as other. It reads the test files as strictly: every line of IdnaTestV2.txt has its seven fields, its status codes take the forms the file’s header names, and its one escape is \uXXXX; every line of NormalizationTest.txt has its five columns of code points; and each entry of test/known_failures.json names a line of IdnaTestV2.txt by its number and its source, once, with a reason, for the pinned version of Unicode. Two runs write the same bytes. Its cache is bin/unicode/17.0.0/ of the superproject, which it fills from unicode.org and iana.org when a file is missing. The data are Unicode’s, under the Unicode License v3, whose notice is in the appendix "Unicode data".

The own lane tables runs the generator again, into the build directory, and compares each of its outputs with the committed file, byte for byte, naming each that differs; update-generated writes them in place, and is the one writer of the generated files. The ordinary lanes never run the generator, which may need the network; the CI runs the lane on its own.

b2 -a libs/idna/test//tables
b2 libs/idna/test//update-generated

What the tables cost

Only <webcpp/idna/detail/properties.hpp> includes the tables, and of the public headers only uts46.hpp and validity.hpp reach it, with <webcpp/idna.hpp>, which includes them: punycode.hpp, errors.hpp and options.hpp cost nothing of them. The tables hold 147,324 bytes of data, and a program links only those it reads. A translation unit that includes <webcpp/idna.hpp> and calls to_ascii, compiled at -O2, takes about 0.7 s of processor time and makes an object of 188 KB natively with Apple Clang 21, and about 0.8 s and 197 KB for wasm32-wasip2 with wasi-sdk 34, against 0.14 s and 0.07 s, and under 1 KB, for the same program without idna. Each time is the least of fifteen compilations, user and system time, on an Apple M5 Max shared with other work.

Moving to another version of Unicode

A move is one change of its own, which updates every place that holds the version, in this order:

  1. tools/unicode.json: its version, and the url and the sha256 of each of Unicode’s files. IANA’s idna-tables-12.0.0 stays as it is. test/known_failures.json: its unicode, and no entry, since each names a line of the old IdnaTestV2.txt.

  2. tools/generate_tables.py, run from the superproject’s root by b2 libs/idna/test//update-generated, which downloads the new files. A property value the new version adds, most often a script, stops it by name. The value then joins the generator’s list of the values it reads as other, SCRIPT_OTHERS, BIDI_OTHERS, JOINING_OTHERS or OTHER_CATEGORIES, once it is known not to matter to IDNA, or the values the tables keep, SCRIPTS, BIDI_CLASSES or JOINING_TYPES, with the code that reads them, when it does; the comment above those lists names the version they were read from. Any other check that fails is an assumption of idna’s that no longer holds, and is settled before the move goes on. Once every check passes, update-generated writes include/webcpp/idna/detail/tables.hpp and the vectors again.

  3. include/webcpp/idna/options.hpp: unicode_version, which detail/properties.hpp refuses to compile while it differs from the tables’; the version that test/options_test.cpp and test/properties_test.cpp pin; and the counts and line numbers of the test files that test/vectors_test.cpp pins.

  4. doc/idna.adoc: the attributes :unicode-version: and :uts46-revision:, which every mention of the version and of UTS 46’s revision on this page reads.

  5. The text that names the version: the description of meta/libraries.json, the first paragraph of README.md, and the section "Unicode and the tables" of AGENTS.md.

  6. Every lane, the own lane tables among them, and the known failures of test/known_failures.json brought up to date, each with its reason, and update-generated run again, which writes each reason into its row.

  7. The changelog, in a line that names the new version.

Guarantees

Each guarantee keeps its number and its anchor; code, tests and comments cite them.

  1. No span form of a conversion and no validator allocates, whatever its input; a pmr form allocates from its memory resource alone.

  2. No conversion, validator or Punycode function throws but the allocation of the memory resource a pmr form was given. The category’s message(int) returns a std::string, as Boost.System’s error_category declares it, so it may throw what the global operator new throws. The member message(char*, std::size_t) of error_code, which calls the category’s message(int, char*, std::size_t), copies the message into a caller’s buffer and throws nothing.

  3. The same input and options give the same output and the same error on every target.

  4. With verify_dns_length, to_ascii returns at most idna::max_domain_length chars, and reports a name whose ASCII form is longer as domain_too_long, unless an earlier error comes first in the order of the errors (To ASCII), and never as output_too_small in a buffer of that size. After an error, the buffer of the span form of to_ascii, as of a Punycode span form, holds nothing to read; the span form of to_unicode goes on after an error, as UTS 46 asks, and the size chars of its unicode_result are the name as the processing left it.

  5. The tables are exactly what tools/generate_tables.py writes from the pinned Unicode 17.0.0 files.

Testing

How idna is tested

idna’s tests are lightweight_test programs, one per subject, under test/, each declared in test/Jamfile with webcpp’s webcpp.run. Every vector of the conformance files is compiled into its test, so no test reads a file, and every test runs on every target.

Test What it checks

errors

The values of idna::error, which are fixed, their messages, the category’s name and identity, and the message cut to a caller’s buffer.

options

The strict profile’s defaults, the designated initializers of the aggregate, the DNS lengths and the version of Unicode.

utf8

Strict UTF-8: the boundaries of every row of RFC 3629’s table of well-formed sequences, decoded and encoded back, every kind of ill-formed one, and the end of the text.

punycode

RFC 3492 section 7.1’s samples, (A) to (S), both ways, and inputs that tell apart what the samples leave alike in the bias; digits in either case and the mixed-case annotation kept; a leading delimiter, a bad digit, a number cut short, a value out of Unicode or a surrogate; each overflow of RFC 3492 section 6.4, in the decoder and the encoder; output_too_small at every length below the result’s; the pmr forms.

properties

One value of every generated table, through its lookup.

vectors

The generated vectors: every line of both files, the blank fields of IdnaTestV2.txt resolved, its lone surrogate kept ill-formed, the lines of the strict pass.

known_failures

The verdict a conformance test gives a line of IdnaTestV2.txt, held against test/known_failures.json: built twice more with webcpp.run-fail, it must fail on a listed line that passes and on an unlisted line that fails.

nfc

NFC within a label’s storage against all 20,034 lines of NormalizationTest.txt, its quick check and its slow path, Hangul, and the storage, inline and growing: its edge after decomposition, at the capacity and one past it, canonical ordering against a stable sort, and a run of 10,000 non-starters normalized in less than 20 times the time of a text as long without one, which an insertion sort exceeds by far.

context, bidi

Each rule of RFC 5892 appendix A and of RFC 5893 section 2, satisfied and broken, the joining scripts a hand-written list misses among them, and A.7 to A.9 over labels of thousands of code points in less than 1,000 times the time of a label as long without a CONTEXTO code point.

process

The label driver in both forms on what ends a label early: an A-label that decodes to nothing, a label beyond the storage after an earlier error, gathered or in the NFC check’s copy, and the empty labels, the root label among them, an undecodable label kept, and the ASCII form as given; and that it throws only what a memory resource throws. Like every test, built with the standard library’s own checks on, so that a read past an empty label traps.

validity

check_label and check_domain: every rule with its error, and of two broken rules the earlier; the strings of the JSON Schema suite’s idn-hostname that UTS 46 alone accepts, as U-labels and as A-labels; A-labels in either case, not ASCII, decoding to ASCII, and labels that only look like one; the lengths at 63 and 64, 253 and 254; the inline storage’s limit; the first error of a name with several; a name of 65,536 labels checked in less than 20 times the time of a name as long of labels of 255 letters; and the rows of IdnaTestV2.txt in both passes, as given: each output the file records without an error is valid, its A-labels in uppercase too, and a source already in its processed form, or in its ASCII form, has the verdict the file gives it.

uts46

All 6,391 lines of IdnaTestV2.txt, toUnicode, toAsciiN and toAsciiT, in the span and the pmr forms, under UTS 46 alone as the file assumes, then the strict profile over the lines whose toUnicode field holds no NV8, XV8 or CONTEXTO code point; and the orders of the errors, the same in both forms under the DNS lengths. Then the pmr forms’ bound on a label’s Punycode, 256 code points: the decoder stops at it, so an A-label of 300 code points followed by a character that is no digit is punycode_overflow, where a decoder that read it whole would find the character first; and a label of 16,384 distinct ideographs is refused in less than 10 times the time of a label as long of one, which the unbounded encoder exceeds by far.

allocation

The global operator new, replaced for this program alone: no call in a span form or a validator, and in a pmr form only those of its memory resource (guarantee 1).

Beside them, test/Jamfile compiles each of the 15 headers alone, and config.hpp three times: as the build asks (config), with WEBCPP_NO_EXCEPTIONS defined for every webcpp library (config_all_off), and with WEBCPP_IDNA_NO_EXCEPTIONS defined by the developer (config_developer_defined). example/Jamfile builds every program this page shows, runs it, and compares what it printed with the .expected beside it.

Conformance, and known failures

IdnaTestV2.txt is UTS 46’s own conformance file. idna passes every line of it, which test/known_failures.json records as an empty list: a line listed there must fail in the pass under UTS 46 alone, which the strict pass then leaves out, and every other line must pass, so the list never lies. The one kind of entry it may hold is a line that fails because of a difference between two versions of Unicode, the file’s and the tables’, with a reason that names both. The lines with a lone surrogate, which no UTF-8 string holds, are refused as invalid_utf8, as the file allows. JSON Schema’s own tests of hostname and idn-hostname run in webcpp::lexical, against the validators it builds on idna.

Targets and exceptions

test/Jamfile and example/Jamfile declare webcpp.targets native wasip2 wasip3 emscripten, and b2 builds each program for those alone: natively with any toolset, for wasm32-wasip2 and wasm32-wasip3 with the toolsets clang-wasip2 and clang-wasip3 run by wasmtime, and with Emscripten run by Node.

<webcpp/idna/config.hpp>, which every public header includes first, defines WEBCPP_IDNA_NO_EXCEPTIONS when webcpp has no exceptions (WEBCPP_HAS_EXCEPTIONS is not defined: the compiler has none, or the program defined WEBCPP_NO_EXCEPTIONS), and a program may define it for idna alone, as Boost.Asio’s BOOST_ASIO_NO_EXCEPTIONS. idna raises no exception of its own (guarantee 2), so the macro changes nothing in it: it exists so that a program can set it for every webcpp library alike. The lint compiles every header, test and example a second time without exceptions.

From the root of a webcpp checkout, natively:

b2 libs/idna/test libs/idna/example

builds the 34 programs of test/Jamfile, the tests above, the two run-fail builds of known_failures, each header alone and config.hpp three times, and runs every test among them; and it builds the 4 examples, runs each and compares its output. For wasm32-wasip2, 34 programs and 4 examples, run by wasmtime; toolset=clang-wasip3 does the same for wasm32-wasip3:

b2 libs/idna/test libs/idna/example toolset=clang-wasip2 testing.launcher=wasmtime

With Emscripten, whose programs b2 runs with Node itself, 34 programs and 4 examples:

b2 libs/idna/test libs/idna/example toolset=emscripten

The own lane tables runs the generator again and compares its outputs with the committed files (The generator):

b2 -a libs/idna/test//tables

The test matrix of every lane is at https://webcpporg.github.io/report/.

Reference

Every name idna declares, in the namespaces webcpp::idna and webcpp::idna::punycode, from the Doc Comments of its headers: each function, type, enumerator and constant with its declaration, what it does, what it asks of its caller and how it fails. The other sections teach the library; this one is where a name is looked up.

Macros

Name Description

WEBCPP_IDNA_NO_EXCEPTIONS

Defined when idna is built without exceptions: when webcpp has none (the compiler has none, or the developer defined WEBCPP_NO_EXCEPTIONS for every webcpp library), or when the developer defines it alone, to disable idna’s in a build that has them.

webcpp::idna

Namespaces

Name Description

punycode

Punycode (RFC 3492), the encoding of an A-label’s code points after its "xn--" prefix, which UTS 46 and IDNA2008 apply to one label at a time.

Types

Name Description

options

The flags of UTS 46 section 4, and IDNA2008’s stricter validity.

unicode_result

What to_unicode wrote into a caller’s buffer, and the first error UTS 46 recorded.

unicode_string

The string to_unicode produced into a memory resource, and the first error UTS 46 recorded.

Type Aliases

Name Description

result

What an operation that can fail returns: a T, or the error_code of the failure.

Enums

Name Description

error

The errors idna reports, as boost::system::error_code values of the category error_category.

Functions

Name Description

check_domain

Checks a domain name, in UTF-8, as it is given: every label as check_label checks it, the Bidi rule across the labels, and the DNS lengths.

check_label

Checks one label, in UTF-8, as it is given: a U-label, an A-label or an NR-LDH label.

error_category

The category of every error.

is_valid_domain

Whether check_domain finds no error in domain.

is_valid_label

Whether check_label finds no error in label.

make_error_code

Makes the error_code of an error, as its implicit conversion does.

to_ascii

to_ascii overloads

to_unicode

to_unicode overloads

Variables

Name Description

max_domain_length

The longest domain name in its ASCII form, in octets, without a trailing root label.

max_label_length

The longest label of a domain name in its ASCII form, in octets (RFC 1034 section 3.1).

unicode_version

The version of Unicode whose data the tables were generated from.

webcpp::idna::punycode

Punycode (RFC 3492), the encoding of an A-label’s code points after its "xn--" prefix, which UTS 46 and IDNA2008 apply to one label at a time.

Functions

Name Description

decode

decode overloads

encode

encode overloads

See Also

"Punycode", in the guide.

webcpp::idna::punycode::decode

decode overloads

Synopses

Declared in <webcpp/idna/punycode.hpp>

Decodes Punycode into a string of code points of the given memory resource.

[[nodiscard]]
result<std::pmr::u32string>
decode(
    std::string_view input,
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Decodes Punycode without its "xn--" prefix into code points, into a caller’s buffer (RFC 3492 section 6.2).

[[nodiscard]]
result<std::size_t>
decode(
    std::span<char32_t> output,
    std::string_view input) noexcept;

Return Value

Note

The return value should not be discarded.

Parameters

Name Description

input

The Punycode to decode.

resource

Where the string’s memory comes from.

output

Where the code points are written; nothing is written past it.

See Also

"Allocation", in the guide.

"Limits", in the guide.

"Punycode", in the guide.

webcpp::idna::punycode::decode

Decodes Punycode into a string of code points of the given memory resource.

Synopsis

Declared in <webcpp/idna/punycode.hpp>

[[nodiscard]]
result<std::pmr::u32string>
decode(
    std::string_view input,
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Description

Nothing bounds the input’s length, and the decoding’s time grows with its square, since RFC 3492’s decoder inserts each code point among those before it: a program bounds the length of input it does not trust, as to_unicode bounds a label at 256 code points.

Return Value

The code points, or the error the span form returns but error::output_too_small.

Note

The return value should not be discarded.

Parameters

Name Description

input

The Punycode to decode.

resource

Where the string’s memory comes from.

See Also

"Allocation", in the guide.

"Limits", in the guide.

webcpp::idna::punycode::decode

Decodes Punycode without its "xn--" prefix into code points, into a caller’s buffer (RFC 3492 section 6.2).

Synopsis

Declared in <webcpp/idna/punycode.hpp>

[[nodiscard]]
result<std::size_t>
decode(
    std::span<char32_t> output,
    std::string_view input) noexcept;

Description

The code points before the last delimiter are copied as they are, case included, as RFC 3492’s mixed-case annotation leaves them; the digits after it are read in either case. A delimiter at the very start consumes nothing. At most the size of output code points are decoded, so the decoding’s time grows at most with the input’s length times the size of output.

Note

A non-basic code point before the delimiter is found before anything is written, whatever the size of output; an error in the digits is found as they are decoded, so an output too small for the result may give error::output_too_small first. After an error, the contents of output are unspecified.

Return Value

The number of code points written, or error::invalid_punycode for a character that is not a digit, a non-basic code point before the delimiter, a truncated number, or a value that is basic, a surrogate or above U+10FFFF; error::punycode_overflow; or error::output_too_small.

Note

The return value should not be discarded.

Parameters

Name Description

output

Where the code points are written; nothing is written past it.

input

The Punycode to decode.

See Also

"Punycode", in the guide.

webcpp::idna::punycode::encode

encode overloads

Synopses

Declared in <webcpp/idna/punycode.hpp>

Encodes code points as Punycode into a string of the given memory resource.

[[nodiscard]]
result<std::pmr::string>
encode(
    std::u32string_view input,
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Encodes code points as Punycode, into a caller’s buffer, without the "xn--" prefix (RFC 3492 section 6.3).

[[nodiscard]]
result<std::size_t>
encode(
    std::span<char> output,
    std::u32string_view input) noexcept;

Return Value

Note

The return value should not be discarded.

Parameters

Name Description

input

The code points to encode.

resource

Where the string’s memory comes from.

output

Where the characters are written; nothing is written past it.

See Also

"Allocation", in the guide.

"Limits", in the guide.

"Punycode", in the guide.

webcpp::idna::punycode::encode

Encodes code points as Punycode into a string of the given memory resource.

Synopsis

Declared in <webcpp/idna/punycode.hpp>

[[nodiscard]]
result<std::pmr::string>
encode(
    std::u32string_view input,
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Description

Nothing bounds the input’s length, and the encoding’s time grows with its square, since RFC 3492’s encoder passes over the input once for each distinct code point: a program bounds the length of input it does not trust, as to_ascii bounds a label at 256 code points.

Return Value

The Punycode, or the error the span form returns but error::output_too_small.

Note

The return value should not be discarded.

Parameters

Name Description

input

The code points to encode.

resource

Where the string’s memory comes from.

See Also

"Allocation", in the guide.

"Limits", in the guide.

webcpp::idna::punycode::encode

Encodes code points as Punycode, into a caller’s buffer, without the "xn--" prefix (RFC 3492 section 6.3).

Synopsis

Declared in <webcpp/idna/punycode.hpp>

[[nodiscard]]
result<std::size_t>
encode(
    std::span<char> output,
    std::u32string_view input) noexcept;

Description

Basic code points, below U+0080, are copied as they are, case included, and then a delimiter - when there is any; the digits that follow are lowercase. The encoding stops when output is full, so its time grows at most with the input’s length times the size of output.

Note

A surrogate or a value above U+10FFFF is found before anything is written, whatever the size of output; an overflow is found as the digits are written, so an output too small for the result may give error::output_too_small first. After an error, the contents of output are unspecified.

Return Value

The number of characters written, or error::invalid_punycode for a surrogate or a value above U+10FFFF, error::punycode_overflow when the arithmetic overflows 32 bits, error::output_too_small when output cannot hold the result.

Note

The return value should not be discarded.

Parameters

Name Description

output

Where the characters are written; nothing is written past it.

input

The code points to encode.

See Also

"Punycode", in the guide.

webcpp::idna::result

What an operation that can fail returns: a T, or the error_code of the failure.

Synopsis

Declared in <webcpp/idna/errors.hpp>

template<class T>
using result = boost::system::result<T>;

Description

It is Boost.System’s result. idna throws nothing of its own: every operation of it that can fail returns one, whose error is an error.

Template Parameters

Name Description

T

The type of the value; void for an operation that returns none.

See Also

"Errors", in the guide.

webcpp::idna::options

The flags of UTS 46 section 4, and IDNA2008’s stricter validity.

Synopsis

Declared in <webcpp/idna/options.hpp>

struct options;

Description

The defaults are the strict profile: every check of UTS 46 on, nontransitional processing, the DNS lengths verified, and IDNA2008’s validity on top. An aggregate, so a caller names only what changes: idna::options{.idna2008 = false} is UTS 46 alone.

Data Members

Name Description

check_bidi

CheckBidi: every label of a Bidi domain name satisfies RFC 5893 section 2.

check_hyphens

CheckHyphens: no hyphens in the third and fourth positions, none at either end (UTS 46 V2, V3); when false, only the "xn--" prefix is refused (V4).

check_joiners

CheckJoiners: the joiners U+200C and U+200D satisfy RFC 5892 A.1 and A.2.

idna2008

IDNA2008’s validity on top of UTS 46: a code point UTS 46 marks NV8 or XV8 is refused, and the CONTEXTO rules of RFC 5892 A.3 to A.9 apply.

ignore_invalid_punycode

IgnoreInvalidPunycode: an "xn--" label that does not decode is kept as it is, without an error.

transitional_processing

Transitional_Processing, which UTS 46 deprecates: the deviations are mapped.

use_std3_ascii_rules

UseSTD3ASCIIRules: the only ASCII code points are a-z, 0-9 and the hyphen (UTS 46 U1).

verify_dns_length

VerifyDnsLength: labels of 1 to 63 octets, a name of 1 to 253, and no empty root label.

See Also

"Converting names", in the guide.

webcpp::idna::options::check_bidi

CheckBidi: every label of a Bidi domain name satisfies RFC 5893 section 2.

Synopsis

Declared in <webcpp/idna/options.hpp>

bool check_bidi = true;

webcpp::idna::options::check_hyphens

CheckHyphens: no hyphens in the third and fourth positions, none at either end (UTS 46 V2, V3); when false, only the "xn--" prefix is refused (V4).

Synopsis

Declared in <webcpp/idna/options.hpp>

bool check_hyphens = true;

webcpp::idna::options::check_joiners

CheckJoiners: the joiners U+200C and U+200D satisfy RFC 5892 A.1 and A.2.

Synopsis

Declared in <webcpp/idna/options.hpp>

bool check_joiners = true;

webcpp::idna::options::idna2008

IDNA2008’s validity on top of UTS 46: a code point UTS 46 marks NV8 or XV8 is refused, and the CONTEXTO rules of RFC 5892 A.3 to A.9 apply.

Synopsis

Declared in <webcpp/idna/options.hpp>

bool idna2008 = true;

webcpp::idna::options::ignore_invalid_punycode

IgnoreInvalidPunycode: an "xn--" label that does not decode is kept as it is, without an error.

Synopsis

Declared in <webcpp/idna/options.hpp>

bool ignore_invalid_punycode = false;

webcpp::idna::options::transitional_processing

Transitional_Processing, which UTS 46 deprecates: the deviations are mapped.

Synopsis

Declared in <webcpp/idna/options.hpp>

bool transitional_processing = false;

webcpp::idna::options::use_std3_ascii_rules

UseSTD3ASCIIRules: the only ASCII code points are a-z, 0-9 and the hyphen (UTS 46 U1).

Synopsis

Declared in <webcpp/idna/options.hpp>

bool use_std3_ascii_rules = true;

webcpp::idna::options::verify_dns_length

VerifyDnsLength: labels of 1 to 63 octets, a name of 1 to 253, and no empty root label.

Synopsis

Declared in <webcpp/idna/options.hpp>

bool verify_dns_length = true;

webcpp::idna::unicode_result

What to_unicode wrote into a caller’s buffer, and the first error UTS 46 recorded.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

struct unicode_result;

Data Members

Name Description

error

Empty when the processing succeeded; else its first error, or error::output_too_small when the buffer could not hold the whole result.

size

The number of chars written.

webcpp::idna::unicode_result::error

Empty when the processing succeeded; else its first error, or error::output_too_small when the buffer could not hold the whole result.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

boost::system::error_code error;

webcpp::idna::unicode_result::size

The number of chars written.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

std::size_t size = 0;

webcpp::idna::unicode_string

The string to_unicode produced into a memory resource, and the first error UTS 46 recorded.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

struct unicode_string;

Data Members

Name Description

error

Empty when the processing succeeded; else its first error.

value

The Unicode form, in UTF-8.

webcpp::idna::unicode_string::error

Empty when the processing succeeded; else its first error.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

boost::system::error_code error;

webcpp::idna::unicode_string::value

The Unicode form, in UTF-8.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

std::pmr::string value;

webcpp::idna::error

The errors idna reports, as boost::system::error_code values of the category error_category.

Synopsis

Declared in <webcpp/idna/errors.hpp>

enum class error : int;

Description

One enumerator per rule family, not per UTS 46 status code: the comment of each names the steps of UTS 46, the status codes of IdnaTestV2 and the sections of the RFCs it reports. The values start at 1, so an error_code of value 0 is no error.

Note

The values are fixed: they are never reused for a different meaning, so a stored value keeps its meaning across versions.

Members

Name Description

invalid_utf8

The input is not well-formed UTF-8 (RFC 3629).

disallowed

A code point whose UTS 46 status is neither valid nor, in nontransitional processing, deviation (UTS 46 V7), or a U+002E FULL STOP inside a label (UTS 46 V5).

not_idna2008

A code point UTS 46 marks NV8 or XV8: DISALLOWED in IDNA2008 (options::idna2008).

not_ldh

An ASCII code point outside a-z, 0-9 and the hyphen (UTS 46 U1, options::use_std3_ascii_rules).

not_nfc

A label not in Normalization Form C (UTS 46 V1, RFC 5891 section 5.4).

hyphen_in_third_and_fourth

Hyphens in both the third and the fourth positions (UTS 46 V2, options::check_hyphens).

hyphen_at_start_or_end

A hyphen at the start or the end of a label (UTS 46 V3, options::check_hyphens).

ace_prefix

A label that starts with "xn--" while options::check_hyphens is off (UTS 46 V4).

leading_combining_mark

A label that starts with a combining mark (UTS 46 V6, RFC 5891 section 5.4).

contextj

A joiner outside the context RFC 5892 A.1 and A.2 allow (IdnaTestV2 C1, C2, options::check_joiners).

contexto

A code point outside the context RFC 5892 A.3 to A.9 allow (options::idna2008).

bidi

A Bidi domain name with a label that breaks RFC 5893 section 2 (IdnaTestV2 B1 to B6, options::check_bidi).

invalid_punycode

Punycode that does not decode, an A-label that decodes to nothing or to ASCII alone, or one not in its canonical form (RFC 3492, UTS 46 P4, RFC 5891 section 5.4).

punycode_overflow

Punycode whose arithmetic overflows 32 bits (RFC 3492 section 6.4, UTS 46 A3), or, in the pmr forms of to_ascii and to_unicode, a label whose Punycode would hold more than 256 code points.

empty_label

An empty label (UTS 46 A4_2 and X4_2, options::verify_dns_length).

label_too_long

A label longer than 63 octets in its ASCII form (UTS 46 A4_2, options::verify_dns_length).

domain_too_long

A domain name longer than 253 octets in its ASCII form (UTS 46 A4_1, options::verify_dns_length).

output_too_small

The caller’s span cannot hold the result.

storage_exhausted

A label beyond the inline working storage, with no memory resource to hold it.

See Also

"Errors", in the guide.

webcpp::idna::error::invalid_utf8

The input is not well-formed UTF-8 (RFC 3629).

Synopsis

Declared in <webcpp/idna/errors.hpp>

invalid_utf8 = 1

webcpp::idna::error::disallowed

A code point whose UTS 46 status is neither valid nor, in nontransitional processing, deviation (UTS 46 V7), or a U+002E FULL STOP inside a label (UTS 46 V5).

Synopsis

Declared in <webcpp/idna/errors.hpp>

disallowed = 2

webcpp::idna::error::not_idna2008

A code point UTS 46 marks NV8 or XV8: DISALLOWED in IDNA2008 (options::idna2008).

Synopsis

Declared in <webcpp/idna/errors.hpp>

not_idna2008 = 3

webcpp::idna::error::not_ldh

An ASCII code point outside a-z, 0-9 and the hyphen (UTS 46 U1, options::use_std3_ascii_rules).

Synopsis

Declared in <webcpp/idna/errors.hpp>

not_ldh = 4

webcpp::idna::error::not_nfc

A label not in Normalization Form C (UTS 46 V1, RFC 5891 section 5.4).

Synopsis

Declared in <webcpp/idna/errors.hpp>

not_nfc = 5

webcpp::idna::error::hyphen_in_third_and_fourth

Hyphens in both the third and the fourth positions (UTS 46 V2, options::check_hyphens).

Synopsis

Declared in <webcpp/idna/errors.hpp>

hyphen_in_third_and_fourth = 6

webcpp::idna::error::hyphen_at_start_or_end

A hyphen at the start or the end of a label (UTS 46 V3, options::check_hyphens).

Synopsis

Declared in <webcpp/idna/errors.hpp>

hyphen_at_start_or_end = 7

webcpp::idna::error::ace_prefix

A label that starts with "xn--" while options::check_hyphens is off (UTS 46 V4).

Synopsis

Declared in <webcpp/idna/errors.hpp>

ace_prefix = 8

webcpp::idna::error::leading_combining_mark

A label that starts with a combining mark (UTS 46 V6, RFC 5891 section 5.4).

Synopsis

Declared in <webcpp/idna/errors.hpp>

leading_combining_mark = 9

webcpp::idna::error::contextj

A joiner outside the context RFC 5892 A.1 and A.2 allow (IdnaTestV2 C1, C2, options::check_joiners).

Synopsis

Declared in <webcpp/idna/errors.hpp>

contextj = 10

webcpp::idna::error::contexto

A code point outside the context RFC 5892 A.3 to A.9 allow (options::idna2008).

Synopsis

Declared in <webcpp/idna/errors.hpp>

contexto = 11

webcpp::idna::error::bidi

A Bidi domain name with a label that breaks RFC 5893 section 2 (IdnaTestV2 B1 to B6, options::check_bidi).

Synopsis

Declared in <webcpp/idna/errors.hpp>

bidi = 12

webcpp::idna::error::invalid_punycode

Punycode that does not decode, an A-label that decodes to nothing or to ASCII alone, or one not in its canonical form (RFC 3492, UTS 46 P4, RFC 5891 section 5.4).

Synopsis

Declared in <webcpp/idna/errors.hpp>

invalid_punycode = 13

webcpp::idna::error::punycode_overflow

Punycode whose arithmetic overflows 32 bits (RFC 3492 section 6.4, UTS 46 A3), or, in the pmr forms of to_ascii and to_unicode, a label whose Punycode would hold more than 256 code points.

Synopsis

Declared in <webcpp/idna/errors.hpp>

punycode_overflow = 14

webcpp::idna::error::empty_label

An empty label (UTS 46 A4_2 and X4_2, options::verify_dns_length).

Synopsis

Declared in <webcpp/idna/errors.hpp>

empty_label = 15

webcpp::idna::error::label_too_long

A label longer than 63 octets in its ASCII form (UTS 46 A4_2, options::verify_dns_length).

Synopsis

Declared in <webcpp/idna/errors.hpp>

label_too_long = 16

webcpp::idna::error::domain_too_long

A domain name longer than 253 octets in its ASCII form (UTS 46 A4_1, options::verify_dns_length).

Synopsis

Declared in <webcpp/idna/errors.hpp>

domain_too_long = 17

webcpp::idna::error::output_too_small

The caller’s span cannot hold the result.

Synopsis

Declared in <webcpp/idna/errors.hpp>

output_too_small = 18

webcpp::idna::error::storage_exhausted

A label beyond the inline working storage, with no memory resource to hold it.

Synopsis

Declared in <webcpp/idna/errors.hpp>

storage_exhausted = 19

webcpp::idna::check_domain

Checks a domain name, in UTF-8, as it is given: every label as check_label checks it, the Bidi rule across the labels, and the DNS lengths.

Synopsis

Declared in <webcpp/idna/validity.hpp>

[[nodiscard]]
result<void>
check_domain(
    std::string_view domain,
    options const& opts = options()) noexcept;

Description

Labels are separated by U+002E alone. Nothing allocates.

Return Value

Nothing, or the first error found: the labels’ in their order, then error::bidi, then error::domain_too_long. error::storage_exhausted for a label beyond the inline storage when opts.verify_dns_length is off.

Note

The return value should not be discarded.

Parameters

Name Description

domain

The domain name.

opts

The checks to apply; the defaults are the strict profile.

See Also

"Validity", in the guide.

"Limits", in the guide.

webcpp::idna::check_label

Checks one label, in UTF-8, as it is given: a U-label, an A-label or an NR-LDH label.

Synopsis

Declared in <webcpp/idna/validity.hpp>

[[nodiscard]]
result<void>
check_label(
    std::string_view label,
    options const& opts = options()) noexcept;

Description

An A-label, "xn--" in any ASCII case, is decoded, validated as a U-label, re-encoded and compared with its input, ASCII letters in either case. Nothing else is mapped, so a label not in NFC, or holding a code point UTS 46 would map (an ASCII capital letter outside an A-label among them), is refused. The Bidi rule is applied to the label as to a domain of one label. An empty label is refused under opts.verify_dns_length and accepted without it, as UTS 46 says. Nothing allocates.

Return Value

Nothing, or the first error found, in the order of the guide: error::disallowed for a U+002E inside label, error::storage_exhausted for a label beyond the inline storage when opts.verify_dns_length is off.

Note

The return value should not be discarded.

Parameters

Name Description

label

The label, without a separator.

opts

The checks to apply; the defaults are the strict profile.

See Also

"Validity", in the guide.

webcpp::idna::error_category

The category of every error.

Synopsis

Declared in <webcpp/idna/errors.hpp>

boost::system::error_category const&
error_category() noexcept;

Description

Its name() is "webcpp.idna", and its message(value) the human message of the error, "invalid Punycode" for error::invalid_punycode, or "unknown webcpp.idna error" for a value error does not define.

Return Value

The category, a constant with a fixed identity.

See Also

"Errors", in the guide.

webcpp::idna::is_valid_domain

Whether check_domain finds no error in domain.

Synopsis

Declared in <webcpp/idna/validity.hpp>

[[nodiscard]]
bool
is_valid_domain(
    std::string_view domain,
    options const& opts = options()) noexcept;

Return Value

check_domain(domain, opts).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

domain

The domain name.

opts

The checks to apply.

See Also

"Validity", in the guide.

webcpp::idna::is_valid_label

Whether check_label finds no error in label.

Synopsis

Declared in <webcpp/idna/validity.hpp>

[[nodiscard]]
bool
is_valid_label(
    std::string_view label,
    options const& opts = options()) noexcept;

Return Value

check_label(label, opts).has_value().

Note

The return value should not be discarded.

Parameters

Name Description

label

The label.

opts

The checks to apply.

See Also

"Validity", in the guide.

webcpp::idna::make_error_code

Makes the error_code of an error, as its implicit conversion does.

Synopsis

Declared in <webcpp/idna/errors.hpp>

boost::system::error_code
make_error_code(error e) noexcept;

Return Value

e as an error_code of the category error_category.

Parameters

Name Description

e

The error.

webcpp::idna::to_ascii

to_ascii overloads

Synopses

Declared in <webcpp/idna/uts46.hpp>

Converts a domain name to its ASCII form into a string of the given memory resource, which also holds a label beyond the inline working storage.

[[nodiscard]]
result<std::pmr::string>
to_ascii(
    std::string_view domain,
    options const& opts = options(),
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Converts a domain name to its ASCII form (UTS 46 section 4.2), into a caller’s buffer.

[[nodiscard]]
result<std::size_t>
to_ascii(
    std::span<char> output,
    std::string_view domain,
    options const& opts = options()) noexcept;

Return Value

Note

The return value should not be discarded.

Parameters

Name Description

domain

The domain name, in UTF-8.

opts

The processing’s flags.

resource

Where the string’s memory, and a long label’s, comes from.

output

Where the ASCII form is written.

See Also

"Allocation", in the guide.

"Converting names", in the guide.

"Limits", in the guide.

webcpp::idna::to_ascii

Converts a domain name to its ASCII form into a string of the given memory resource, which also holds a label beyond the inline working storage.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

[[nodiscard]]
result<std::pmr::string>
to_ascii(
    std::string_view domain,
    options const& opts = options(),
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Description

Under opts.verify_dns_length no label beyond the inline storage can pass, so such a label is refused as the span form refuses it, and both forms report the same error. Without it, a label of any length is converted, but a label of more than 256 code points that is not ASCII, or an A-label whose Punycode decodes to more, is error::punycode_overflow, whatever opts.ignore_invalid_punycode says, since Punycode’s work grows with the square of a label, and the bound keeps it a small multiple of the rest of the processing.

Note

Throws only what resource throws when it cannot allocate.

Return Value

The ASCII form, or the first error, as the span form orders them.

Note

The return value should not be discarded.

Parameters

Name Description

domain

The domain name, in UTF-8.

opts

The processing’s flags.

resource

Where the string’s memory, and a long label’s, comes from.

See Also

"Allocation", in the guide.

webcpp::idna::to_ascii

Converts a domain name to its ASCII form (UTS 46 section 4.2), into a caller’s buffer.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

[[nodiscard]]
result<std::size_t>
to_ascii(
    std::span<char> output,
    std::string_view domain,
    options const& opts = options()) noexcept;

Description

The name is mapped, normalized to NFC, split into labels, and each label is validated and, when it is not ASCII, written as "xn--" and its Punycode. With opts.verify_dns_length, the default, the result never exceeds max_domain_length, so a std::array<char, max_domain_length> always holds it. Nothing allocates.

Note

After an error, the contents of output are unspecified.

Return Value

The number of chars written, or the first error found, a label’s in their order, then error::bidi, error::domain_too_long, and error::output_too_small last. error::storage_exhausted for a label beyond the inline storage when opts.verify_dns_length is off.

Note

The return value should not be discarded.

Parameters

Name Description

output

Where the ASCII form is written.

domain

The domain name, in UTF-8.

opts

The processing’s flags; the defaults are the strict profile.

See Also

"Converting names", in the guide.

"Limits", in the guide.

webcpp::idna::to_unicode

to_unicode overloads

Synopses

Declared in <webcpp/idna/uts46.hpp>

Converts a domain name to its Unicode form into a string of the given memory resource, as far as it can.

[[nodiscard]]
unicode_string
to_unicode(
    std::string_view domain,
    options const& opts = options(),
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Converts a domain name to its Unicode form (UTS 46 section 4.3), into a caller’s buffer, as far as it can.

[[nodiscard]]
unicode_result
to_unicode(
    std::span<char> output,
    std::string_view domain,
    options const& opts = options()) noexcept;

Return Value

  • The Unicode form, and the first error recorded.

  • What was written, and the first error recorded; a label beyond the inline storage stops the writing there with error::storage_exhausted, after any earlier label’s error.

Note

The return value should not be discarded.

Parameters

Name Description

domain

The domain name, in UTF-8.

opts

The processing’s flags.

resource

Where the string’s memory, and a long label’s, comes from.

output

Where the Unicode form is written, in UTF-8; the writing stops at a code point boundary when it is full.

See Also

"Allocation", in the guide.

"Converting names", in the guide.

"Limits", in the guide.

webcpp::idna::to_unicode

Converts a domain name to its Unicode form into a string of the given memory resource, as far as it can.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

[[nodiscard]]
unicode_string
to_unicode(
    std::string_view domain,
    options const& opts = options(),
    std::pmr::memory_resource& resource = *std::pmr::get_default_resource());

Description

A label of any length is converted, but an A-label whose Punycode decodes to more than 256 code points is kept as it is, with error::punycode_overflow whatever opts.ignore_invalid_punycode says, since the decoder’s work grows with the square of the label and a limit of the implementation is no invalid Punycode.

Note

Throws only what resource throws when it cannot allocate.

Return Value

The Unicode form, and the first error recorded.

Note

The return value should not be discarded.

Parameters

Name Description

domain

The domain name, in UTF-8.

opts

The processing’s flags.

resource

Where the string’s memory, and a long label’s, comes from.

See Also

"Allocation", in the guide.

webcpp::idna::to_unicode

Converts a domain name to its Unicode form (UTS 46 section 4.3), into a caller’s buffer, as far as it can.

Synopsis

Declared in <webcpp/idna/uts46.hpp>

[[nodiscard]]
unicode_result
to_unicode(
    std::span<char> output,
    std::string_view domain,
    options const& opts = options()) noexcept;

Description

As UTS 46 says, the conversion goes on after an error: each label is written as the processing leaves it, mapped and normalized, an A-label decoded when its Punycode decodes and kept as it is otherwise; no U+FFFD is inserted. Nothing allocates.

Return Value

What was written, and the first error recorded; a label beyond the inline storage stops the writing there with error::storage_exhausted, after any earlier label’s error.

Note

The return value should not be discarded.

Parameters

Name Description

output

Where the Unicode form is written, in UTF-8; the writing stops at a code point boundary when it is full.

domain

The domain name, in UTF-8; ill-formed UTF-8 writes nothing.

opts

The processing’s flags; the defaults are the strict profile.

See Also

"Converting names", in the guide.

"Limits", in the guide.

webcpp::idna::max_domain_length

The longest domain name in its ASCII form, in octets, without a trailing root label.

Synopsis

Declared in <webcpp/idna/options.hpp>

inline constexpr std::size_t max_domain_length = 253;

webcpp::idna::max_label_length

The longest label of a domain name in its ASCII form, in octets (RFC 1034 section 3.1).

Synopsis

Declared in <webcpp/idna/options.hpp>

inline constexpr std::size_t max_label_length = 63;

webcpp::idna::unicode_version

The version of Unicode whose data the tables were generated from.

Synopsis

Declared in <webcpp/idna/options.hpp>

inline constexpr std::string_view unicode_version = "17.0.0";

WEBCPP_IDNA_NO_EXCEPTIONS

Defined when idna is built without exceptions: when webcpp has none (the compiler has none, or the developer defined WEBCPP_NO_EXCEPTIONS for every webcpp library), or when the developer defines it alone, to disable idna’s in a build that has them.

Synopsis

Declared in <webcpp/idna/config.hpp>

#define WEBCPP_IDNA_NO_EXCEPTIONS

Description

idna raises no exception of its own: every operation that can fail returns its error, so the macro changes nothing in it. It is provided so that a program can set it for every webcpp library alike. An exception idna raised would go through boost::throw_exception, and an API that throws would be absent while this is defined.

See Also

"Testing", in the guide.

Appendix A: Unicode data

idna’s tables, <webcpp/idna/detail/tables.hpp>, and the conformance vectors of its tests, test/generated/, are derived from the Unicode Character Database and the files of UTS 46, version 17.0.0, which Unicode, Inc. publishes under the following notice. idna’s code is under the Boost Software License 1.0 alone.

UNICODE LICENSE V3

COPYRIGHT AND PERMISSION NOTICE

Copyright © 1991-2026 Unicode, Inc.

NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.

THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.

Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.

Appendix B: Changelog

What changed in idna in each version of webcpp, newest first, one line per change. "Unreleased" is what the next version brings. The release notes of every version are on webcpp’s index page.

Unreleased

webcpp 0.1.0

  • Added the library: internationalized domain names over Unicode 17.0.0 tables.

  • Added Punycode encoding and decoding, into a caller’s buffer or a pmr string.

  • Added UTS 46 ToASCII and ToUnicode, passing every line of IdnaTestV2.

  • Added IDNA2008 validity of labels and domains, with its contextual and Bidi rules.

  • Added errors as values with a fixed category, and options for every check.

  • Added the WEBCPP_IDNA_NO_EXCEPTIONS configuration macro, which WEBCPP_NO_EXCEPTIONS also defines.