Iconvex

2,105 codecs. Ten runtime packages. Pure Elixir/BEAM. Zero NIFs, ports, operating-system iconv, or external processes.

Hex.pmHexDocsCILicense

Iconvex provides a native BEAM port of GNU libiconv 1.19. It includes every codec that --enable-extra-encodings selects.

The wider collection covers GSM septets, Morse, Baudot, UTF-9, UTF-18, Punycode, SCSU, MARC-8, PETSCII, ATASCII, DEC formats, and UNIVAC FIELDATA. It also covers punched cards, TeX fonts, vendor pages, current ICU converters, and 1,050 historical ICU revisions.

Each historical ICU name includes its source filename. This rule keeps incompatible revisions separate.

Core and Extras match all 198 GNU libiconv 1.19 fixed codecs. Core supplies 112 default codecs. Extras supplies 86 optional codecs. The wider Iconvex ecosystem supplies 2,105 unique canonical codecs.

Use the standard conversion API:

{:ok, "cafΓ©"} =
Iconvex.convert(<<0x63, 0x61, 0x66, 0xE9>>, "CP1252", "UTF-8")

Package APIs return exact Elixir bitstrings for non-octet formats. They use self-describing LSB containers when the bit order requires one.

Package selection

PackageCanonical codecsCoverage
iconvex112GNU default: Unicode, CJK, ISO-2022, HZ, Windows/Mac/ISO pages, C99/JAVA, transliteration, generic 1–8-bit packing
iconvex_extras86Complete GNU --enable-extra-encodings: AIX, DOS, OSF/1, z/OS EBCDIC, Atari ST, RISC OS, DEC Asian, JIS X 0213
iconvex_telecom61GSM 03.38 national tables, Morse, Baudot/ITA, SITOR/NAVTEX, AIS6, TBCD, SIM alphabets, broadcast and teleprinter formats
iconvex_unicode17BOCU-1, CESU-8, SCSU, UTF-EBCDIC, UTF-1/5/6, WTF-8, signature profiles, plus native UTF-9 and UTF-18 APIs
iconvex_standards248RFC 1345, Punycode, IMAP UTF-7, ISCII, ISO-IR, ECMA, PCL, Unihan and standards-defined transforms
iconvex_bibliographic10MARC-8, ANSEL, Cork/T1, TeX OT1/OML/OMS, PostScript and glyph-vector mappings
iconvex_retro192PETSCII, ATASCII, Apple/DEC/CDC/UNIVAC, terminals, calculators, punched cards and 57 packed profiles
iconvex_platforms117IBM, Microsoft, .NET, Java, CPython, glibc, Kermit, Adobe, Mac and other vendor-defined compatibility surfaces
iconvex_icu212Current ICU-derived SBCS/MBCS/stateful tables, LMBCS variants, Unicode profiles and compound text
iconvex_icu_archive1,050Source-filename-qualified historical ICU revisions, backed by six byte-balanced data packages

You can install each package separately. Each package depends only on Core. The Archive facade depends on six exact-version provider-only data packages. A leaf registers its complete codec set in one transaction. Install only the families that your application uses.

iconvex_specs is an unpublished generation workspace. It supports research, conformance, and integration. It owns no runtime codec. Do not add it as an application dependency.

The repository root contains ICONVEX_CODEC_PACKAGE_INDEX.md and ICONVEX_CODEC_PACKAGE_INDEX.csv. These generated files map the complete codec set. ICONVEX_FULL_STACK_SUPPORT.md lists ownership and GNU status for all 2,105 names. PACKAGE_ARCHITECTURE.md describes dependencies, lifecycle, artifacts, and release order.

Installation

Core only:

def deps do
[{:iconvex, "~> 0.1"}]
end

Exact GNU fixed-codec surface:

def deps do
[
{:iconvex, "~> 0.1"},
{:iconvex_extras, "~> 0.1"}
]
end

Choose any leaves:

def deps do
[
{:iconvex, "~> 0.1"},
{:iconvex_telecom, "~> 0.1"},
{:iconvex_unicode, "~> 0.1"},
{:iconvex_standards, "~> 0.1"},
{:iconvex_bibliographic, "~> 0.1"},
{:iconvex_retro, "~> 0.1"},
{:iconvex_platforms, "~> 0.1"},
{:iconvex_icu, "~> 0.1"}
]
end

For the historical archive, add only the facade:

{:iconvex_icu_archive, "~> 0.1"}

Mix fetches iconvex_icu_archive_data_01 through iconvex_icu_archive_data_06 transitively. Those packages own table storage, not additional codec names.

Encoding examples

Integration tests execute each example.

Core: escapes, state machines, and Unicode

Core owns GNU's 112 default codecs and accepts 446 normalized names. C99 and JAVA source escapes are codecs. HZ uses a state machine.

{:ok, ~S(\u00e9\U0001f600)} =
Iconvex.convert("Γ©πŸ˜€", "UTF-8", "C99")
{:ok, "~{VP~}~~{ND~}"} =
Iconvex.convert("δΈ­~ζ–‡", "UTF-8", "HZ")
[<<"caf", 0xC3>>, <<0xA9>>]
|> Iconvex.stream!("UTF-8", "CP1252")
|> Enum.join()
#=> <<0x63, 0x61, 0x66, 0xE9>>

Core includes UTF-7/8/16/32, UCS-2/4, GB18030:2005, GB18030:2022, EUC-JP/KR/TW, Big5/HKSCS, Shift_JIS, JOHAB, TCVN, and VISCII. It also includes the complete ISO-2022-JP/JP-1/JP-2/JP-MS/KR/CN/CN-EXT family.

Extras: GNU optional encodings

Extras is the exact 86-codec complement behind GNU's optional definition families. It includes DOS pages, AIX, OSF/1, 53 z/OS pages, ATARIST, RISCOS-LATIN1, DEC-HANYU, BIG5-2003, JIS X 0213 and ISO-2022-JP-3.

The z/OS UNIX surface makes EBCDIC newline policy explicit:

{:ok, ebcdic} =
Iconvex.convert("hello\n", "ASCII", "IBM-1047/ZOS_UNIX")
<<0x88, 0x85, 0x93, 0x93, 0x96, 0x15>> = ebcdic
{:ok, "hello\n"} = Iconvex.convert(ebcdic, "IBM-1047/ZOS_UNIX", "ASCII")

Extras provides IBM-423, the Greek EBCDIC codec from the original glibc implementation.

Telecom: septets, Baudot, Morse, and radio alphabets

Telecom owns 61 byte codecs and 53 packed profiles spanning 3, 5, 6, and 7 bits. International Morse follows ITU-R M.1677:

{:ok, "... --- ..."} = Iconvex.convert("SOS", "UTF-8", "MORSE-CODE")
{:ok, "SOS"} = Iconvex.convert("... --- ...", "MORSE-CODE", "UTF-8")
# MORSE-CODE resolves to MORSE-ITU-M1677.

GSM 03.38's registry surface uses one septet per octet, exactly as SMPP does. The high bit stays clear:

{:ok, <<0x04, 0x0B>>} = Iconvex.convert("β‚¬Δž", "UTF-8", "GSM0338-TURKISH")
{:ok, "β‚¬Δž"} = Iconvex.convert(<<0x04, 0x0B>>, "GSM0338-TURKISH", "UTF-8")

All national locking and single-shift tables are available. You can pair these tables independently:

alias Iconvex.Telecom.GSM0338
options = [locking: :portuguese, single_shift: :bengali]
{:ok, [0x00EA, 0x09E6]} = GSM0338.decode(<<0x04, 0x1B, 0x19>>, options)
{:ok, <<0x04, 0x1B, 0x19>>} = GSM0338.encode([0x00EA, 0x09E6], options)
182 = length(GSM0338.valid_pairs())

TPDU packing produces contiguous wire septets:

alias Iconvex.Telecom.GSM0338.Packing
{:ok, packed} = Packing.pack("hellohello")
"E8329BFD4697D9EC37" = Base.encode16(packed)
{:ok, "hellohello"} = Packing.unpack(packed, 10)

The same package covers ITA1/ITA2, Murray 1905, Wheatstone/Hausmann, CCIR476/SITOR/NAVTEX, AIS6, TBCD, SIM/USIM alphabets, ARIB and three Unicode 17 carrier-emoji profiles.

Unicode: nonets, 18-bit words, and compression

Unicode owns BOCU-1, CESU-8, FSS-UTF, SCSU, UTF-EBCDIC, UTF-1, UTF-5, UTF-6, WTF-8 and explicit signature profiles. UTF-9 and UTF-18 are logical non-octet codecs, so their native APIs preserve the specified bit width:

{:ok, utf9} = Iconvex.Unicode.UTF9.encode_packed([?A, 0x0391, 0x10330])
54 = bit_size(utf9)
{:ok, [?A, 0x0391, 0x10330]} = Iconvex.Unicode.UTF9.decode_packed(utf9)
{:ok, utf18} = Iconvex.Unicode.UTF18.encode_packed([?A, 0x10330])
36 = bit_size(utf18)
{:ok, [?A, 0x10330]} = Iconvex.Unicode.UTF18.decode_packed(utf18)

Byte protocols use UTF-9-16BE, UTF-9-16LE, UTF-18-24BE, or UTF-18-24LE. Padding and endianness are part of the name instead of ambient machine state.

Standards: RFCs, ISO registers, and raw Punycode

Punycode implements the raw RFC 3492 transform. It does not add an xn-- DNS prefix.

{:ok, "ihqwcrb4cv8a8dqg056pqjye"} =
Iconvex.convert("δ»–δ»¬δΈΊδ»€δΉˆδΈθ―΄δΈ­ζ–‡", "UTF-8", "PUNYCODE")
{:ok, "δ»–δ»¬δΈΊδ»€δΉˆδΈθ―΄δΈ­ζ–‡"} =
Iconvex.convert("ihqwcrb4cv8a8dqg056pqjye", "PUNYCODE", "UTF-8")

Standards also supplies RFC 1345 identities, IMAP UTF-7, ISCII, KOI variants, ECMA-1/44, PCL sets, ISO-IR registers, and property-token mappings. GNU spellings keep the GNU route when they collide with historical RFC 1345 names. The historical identity keeps an explicit name such as RFC1345:IBM037.

Bibliographic: MARC-8, ANSEL, and TeX machinery

MARC-8 puts combining marks before the base letter. Unicode does the opposite. The codec restores Unicode mark order:

{:ok, "e\u0301"} = Iconvex.convert(<<0xE2, ?e>>, "MARC-8", "UTF-8")

The package also owns ANSEL, Cork/T1, OT1, OML, OMS, Adobe PostScript and AGL glyph-vector mappings, including explicit packed seven-bit math-font profiles.

Retro: six-bit machines, punched cards, and calculator glyphs

The Apple-1 Signetics 2513 address order is @–_, then space–?. The registry exposes one unit per byte. Iconvex.Retro.Packed preserves six bits.

{:ok, <<0, 1, 2, 3, 32, 63>>} =
Iconvex.convert("@ABC ?", "UTF-8", "APPLE-1-SIGNETICS-2513-CM2140-6BIT")
{:ok, packed} =
Iconvex.Retro.Packed.encode_from_utf8(
"@ABC ?",
Iconvex.Retro.Apple1Signetics2513,
:msb
)
36 = bit_size(packed)
{:ok, "@ABC ?"} =
Iconvex.Retro.Packed.decode_to_utf8(
packed,
Iconvex.Retro.Apple1Signetics2513,
:msb
)

DEC RADIX-50 uses a base-40 word:

{:ok, <<0o115402::16-little>>} =
Iconvex.convert("X2B", "UTF-8", "DEC-RADIX-50")

Other codecs include PETSCII, ATASCII, Apple II, ZX80/81/Spectrum, Amstrad, MSX, TRS-80, TI calculators, DEC-SIXBIT, FIELDATA-UNIVAC-1100, CDC display codes, Hollerith and 12-bit punched cards.

Platforms: vendor-qualified profiles

Platforms preserves source-qualified vendor behavior. It does not merge mappings that only appear similar.

{:ok, "\u1E83"} = Iconvex.convert(<<0xFF>>, "MacCeltic", "UTF-8")

Extras owns IBM-423 and GNU's optional IBM pages. Platforms owns additional IBM CCSIDs, Windows best-fit maps, Java modified UTF-8, and .NET or CPython profiles. It also owns glibc charmaps, Adobe mappings, and Mac variants.

ICU: current converter profiles

Current ICU data and algorithm profiles are separate from historical revisions. LMBCS optimization groups remain distinct codecs:

{:ok, <<0xD5>>} = Iconvex.convert("Ξ©", "UTF-8", "LMBCS-2")
{:ok, "Ξ©"} = Iconvex.convert(<<0xD5>>, "LMBCS-2", "UTF-8")

The 212-codec package includes 191 generated table providers plus compound text, JIS, LMBCS and Unicode variant implementations.

ICU Archive: reproducible historical behavior

The Archive owns 1,050 source-filename-qualified revisions. Six provider-only packages limit artifact size without adding duplicate codec identities. The following example uses a stateful IBM-930 revision with SO/SI:

codec = Iconvex.ICUArchive.Codecs.C674
bytes = <<0xC1, 0x0E, 0x44, 0x81, 0x44, 0x82, 0x0F, 0xC2>>
"ICU-ARCHIVE-ibm-930_P120-1999" = codec.canonical_name()
{:ok, ^bytes} = codec.encode(~c"AあいB")
{:ok, ~c"AあいB"} = codec.decode(bytes)

Tests exhaustively check the facade manifest and shard assignment. They also check every table digest and generated decoder/encoder mapping.

Conversion API

Bang functions return the binary directly. Suffixes and explicit options share one implementation:

<<"caf", 0xE9>> = Iconvex.convert!("cafΓ©", "UTF-8", "CP1252")
{:ok, "AB"} = Iconvex.convert("AπŸ˜€B", "UTF-8", "ASCII//NON_IDENTICAL_DISCARD")

You can also pass options explicitly:

Iconvex.convert(input, from, to,
invalid: :discard,
unrepresentable: :discard,
transliterate: true,
byte_substitute: "<0x%02x>",
unicode_substitute: "<U+%04X>",
on_invalid_byte: fn event -> {:replace, "<byte-#{event.byte}>"} end
)

The byte_substitute option replaces malformed source bytes. The unicode_substitute option replaces unsupported destination characters. Both formats accept GNU-style %x and %X fields with zero-padded widths. With iconvex_extras, you can select IBM-1047 newline behavior by name or option.

Default destination handling matches GNU libiconv. Any character causes an error when the target cannot represent it. Unicode-family targets use U+FFFD for supported fallback values. Discard, transliteration, or unicode_substitute takes priority over that fallback.

JAVA and UTF-7 preserve isolated 16-bit surrogate units from the source codec.

Generic UTF-16 and UTF-32 emit a BOM after they emit a target character.

ISO-2022-JP-2 implements GNU's case-insensitive ja, ko, and zh Unicode tags. It supports cancellation and streaming state.

Iconvex.convert(input, "IBM-1047/ZOS_UNIX", "UTF-8")
Iconvex.convert(input, "UTF-8", "IBM-1047", to_surface: :zos_unix)

on_invalid_byte handles source bytes that a codec cannot decode. The callback receives an Iconvex.InvalidByte value. The value contains the canonical encoding, error kind, absolute offset, current byte, and codec error sequence. The callback returns :default, :error, :discard, or {:replace, utf8_or_codepoints}.

Iconvex does not classify an unmapped byte as a display command, corruption, or application control. The file format and caller make that decision. The same callback works with one-shot and lazy Stream conversion.

External fixed-width codecs can declare multi-byte recovery consumption, and whole-string transforms can declare stop-on-error recovery so callbacks never restart a desynchronized suffix. See EXTENDING.md.

Errors contain the source byte offset or unrepresentable Unicode code point:

{:error, %Iconvex.Error{kind: :invalid_sequence, offset: 3}} =
Iconvex.convert(input, "UTF-8", "CP1252")

For lazy bounded-memory conversion, input enumerable emits binaries and output is an Elixir Stream of converted binaries:

output =
File.stream!(path, [], 64 * 1024)
|> Iconvex.stream!("PETSCII", "UTF-8", on_invalid_byte: controls)
|> Enum.into("")

The stream preserves lookahead, source offsets, UTF BOM state, and built-in HZ, ISO-2022, and UTF-7 state across chunk boundaries. It also preserves multi-code-point target mappings.

The UTF-7 decoder commits a shifted run after it validates the padding and UTF-16 payload. A plus sign inside an active shift stays in that Base64 run. The decoder processes byte-at-a-time shifted input in linear time.

ISO-2022 diagnostic frames and JP-MS SO/SI state do not depend on split locations. Output chunks do not need to align with input chunks. Conversion failures raise Iconvex.Error during enumeration because the caller might have consumed earlier output. External codecs use the incremental callbacks in EXTENDING.md.

For a buffered chunked transaction:

{:ok, converter} = Iconvex.new("UTF-8", "SHIFT_JIS")
{:ok, <<>>, converter} = Iconvex.feed(converter, chunk1)
{:ok, <<>>, converter} = Iconvex.feed(converter, chunk2)
{:ok, output, finished} = Iconvex.finish_with_state(converter)
{:error, :already_finished} = Iconvex.feed(finished, chunk3)

Iconvex buffers each source until finish/1 or finish_with_state/1. Each feed takes O(1) time. Finalization converts the accumulated iodata once.

This contract makes chunked output byte-identical to one-shot output for every split. It covers stateful designations, malformed-input policies, Vietnamese composition, multi-code-point mappings, and UTF BOMs. Errors use offsets from the start of the complete stream.

finish/1 keeps the original return shape. Use finish_with_state/1 to get terminal state and repeated-finish rejection.

Use Iconvex.encodings/0 for canonical names and Iconvex.canonical_name/1 to resolve an alias.

Non-octet packed transports

You can pack codecs that expose one complete signal unit per octet. The operation does not copy their mapping logic.

{:ok, packed} = Iconvex.Packed.encode_from_utf8("Hello", "ASCII", 7)
{:ok, "Hello"} = Iconvex.Packed.decode_to_utf8(packed, "ASCII", 7)
{:ok, lsb} = Iconvex.Packed.encode_from_utf8_lsb("hellohello", "ASCII", 7)
"E8329BFD4697D9EC37" = Base.encode16(lsb.data)
{:ok, "hellohello"} = Iconvex.Packed.decode_to_utf8_lsb(lsb, "ASCII")

pack/2 returns an exact MSB-first Elixir bitstring. pack_lsb/2 returns an Iconvex.Packed.LSB value. The value contains byte data, meaningful bit count, and unit width. This metadata removes ambiguity from final-octet padding.

When a codec rejects an unpacked unit, Iconvex.Error uses a physical bit offset. It sets offset_unit: :bit. MSB errors retain the offending bitstring. LSB errors retain a self-describing Iconvex.Packed.LSB fragment.

Tests cover every unit value and each width from 1 through 8 in both orders.

Owning leaves implement wider formats that have codec-specific scalar rules. Unicode owns UTF-9 and UTF-18. Standards, Bibliographic, Retro, and Telecom publish their exact profile catalogs.

External codec libraries

External packages implement Iconvex.Codec, then atomically register a module at startup:

defmodule MyLibrary.Codec do
use Iconvex.Codec
def canonical_name, do: "X-MY-CODEC"
def decode(binary), do: MyLibrary.Decoder.decode(binary)
def decode_discard(binary), do: MyLibrary.Decoder.decode_discard(binary)
def encode(codepoints), do: MyLibrary.Encoder.encode(codepoints)
def encode_discard(codepoints), do: MyLibrary.Encoder.encode_discard(codepoints)
def encode_substitute(codepoints, replacer),
do: MyLibrary.Encoder.encode_substitute(codepoints, replacer)
end
{:ok, ownership} = Iconvex.register_codec_if_absent(MyLibrary.Codec)
{:ok, output} = Iconvex.convert(input, "X-MY-CODEC", "UTF-8")

If ownership is a reference, retain it. On shutdown, pass it to Iconvex.unregister_codec(MyLibrary.Codec, ownership). The :existing result means that another caller owns the registration. Leave that registration in place.

Versioned snapshots preserve registrations and ownership tokens across consecutive registry-worker crashes. The :iconvex application must stay active during recovery. A clean application stop erases the recovery state. Therefore, registrations do not cross a deliberate stop and start boundary. Replacement uses one commit point. Concurrent lookups resolve either the complete old registration or the complete new registration.

Strict per-commit durability writes a synchronous recovery snapshot to :persistent_term. Replacing that term can schedule VM-wide garbage collection. Keep registration on the control plane and outside the request-processing path.

Packages with many codecs should use Iconvex.Extension.register/2. It wraps the internal register_set transaction. It publishes the complete set in one transaction and writes one snapshot. A delayed write would weaken the consecutive worker-crash guarantee.

Mapping tables and their decode accelerators use lazy :persistent_term entries. Loaded application inventories set the maximum resident set. Each table identity has one base term. It also has at most one term for each of three derived cache shapes.

Provider removal erases the base term and all derived keys under their build locks. Repeated package cycles do not accumulate provider cache generations. Keep provider startup and shutdown on the control plane. LRU replacement in the request-processing path would add a VM-wide cost to these immutable caches.

If a declared name collides with another source, use an explicit source-qualified name. That registration uses the replacement canonical name. Iconvex still registers and checks the module aliases and option aliases.

:ok =
Iconvex.register_codec(MyLibrary.Codec,
canonical: "MY-SOURCE-X-MY-CODEC",
aliases: ["MY-SOURCE-MYCODEC"]
)

Consumer configuration can load modules with config :iconvex, external_codecs: [MyLibrary.Codec]. Tuple entries accept the same :canonical and :aliases options. Iconvex.encodings/0 includes the registered codecs. External packages cannot override built-in names.

External OTP applications that own a complete codec inventory and mapping tables can publish both with one public package transaction:

{:ok, token} =
Iconvex.Extension.register(
[MyLibrary.Codec, MyLibrary.OtherCodec],
owner: :my_library,
priority: 10,
table_providers: [:my_mapping]
)
# Keep the opaque token in Application state.
:ok = Iconvex.Extension.unregister(token)

Registration and removal use atomic transactions. Failures roll back newly acquired providers. Supervised worker restarts preserve the exact token. Stale tokens cannot remove replacements.

EXTENDING.md documents:

iconvex_extras provides the reference external codec package. It shares Core engines but keeps optional tables and modules outside Core.

Each of the nine extension facades installs one managed registration set. The integration contract tests forward, reverse, and every cyclic start boundary. Each path produces the same 2,105-name registry.

Canonical claims rank ahead of aliases and then by fixed package priority. Stopping a winner exposes a retained fallback without a lookup gap. Individual third-party registrations through register_codec* remain strict. A distinct module that claims an existing name gets a {:name_conflict, name} error.

The generated ICONVEX_FULL_STACK_SUPPORT.md records the owning package and GNU libiconv status for every canonical name. The repository ICONVEX_CODEC_PACKAGE_INDEX.md document also includes:

Coverage evidence

Checked-in generated inventories supply the counts in this README.

The GNU differential walks one sequential UCS-4BE corpus in four directions. The corpus contains all 1,114,112 code points from U+0000 through U+10FFFF. It includes all scalar values and all 2,048 surrogate code points. The performance gate uses the fastest of three isolated samples. It rejects a supported GNU codec when its result exceeds 30x GNU libiconv.

Each leaf adds source-pinned vectors and full table walks where applicable. It also adds malformed-input policies, streaming checks, packed round trips, lifecycle tests, and artifact contracts. Integration tests execute the README gallery.

The research projection tracks 385 direct Wikipedia-sourced clusters that GNU libiconv 1.19 lacks. Iconvex implements 249 of them.

The audit identifies 12 actionable codec gaps. Another 96 clusters still need exact public mappings or specifications. The final 28 are families, mapping notations, repertoire profiles, or a withdrawn part. They are not standalone byte codecs.

See the generated iconvex/research/WIKIPEDIA_MISSING_FROM_GNU.md disposition report.

Operational and trust boundaries

Verification

mix test
MIX_ENV=prod mix run bench/benchmark.exs
MIX_ENV=prod mix run bench/external_codec_benchmark.exs
GNU_ICONV=/path/to/gnu-libiconv-1.19/bin/iconv \
MIX_ENV=prod mix run tools/exhaustive_unicode_differential.exs

The public source monorepo is edescourtis/iconvex. ExDoc source links use the durable main branch and the iconvex/ subdirectory. These links work before a matching release tag exists.

The repository test-fixture set contains 268 GNU libiconv 1.19 files. It contains 267 byte-exact upstream files plus one derived configured Makefile. The set uses 9.0 MB. Separate and combined SHA-256 values verify its provenance boundary.

The consumer Hex artifact excludes the test-fixture set. It includes generated runtime tables and public conformance documents. Core ExUnit runs the applicable default-codec cases. iconvex_extras runs 92 extra fixtures plus ISO-2022-JP-3 and IBM-1047 cases.

Together, the suites port 164 stateless checks, 27 snippet round trips, the GB18030 supplementary range, policy checks, and four C regression tests. The z/OS filesystem-tag check stays platform-only because upstream skips it outside z/OS.

See UPSTREAM_TEST_COVERAGE.md for the checked traceability matrix. SUPPORTED_ENCODINGS.md lists all 198 codecs with package ownership and GNU status.

EXHAUSTIVE_UNICODE_DIFFERENTIAL.md describes the 112-codec Core run. The Extras report describes the combined 198-codec differential against GNU libiconv 1.19. The sequential UCS-4BE corpus contains all 1,112,064 scalar values and 2,048 surrogate code points. Surrogates are code points, not scalar values. The performance gate uses the fastest of three samples and enforces a 30x-GNU limit.

See BENCHMARKS.md and TDD_LOG.md. DEEP_DIVE_REMEDIATION.md records each independent review finding.

elixir tools/release_check.exs builds the documentation. It unpacks the exact Hex artifact into a temporary directory. It verifies compilation and representative conversions from a clean consumer project.

To regenerate the registry, mappings, extensions, and transliteration table from an unpacked GNU libiconv 1.19 source tree:

elixir tools/generate_registry.exs /path/to/libiconv-1.19
mix format

The generator writes the 112 default codecs into this package and the exact 86-codec complement into the sibling iconvex_extras package.

License

LGPL-2.1-or-later covers Iconvex and its derived GNU mapping data. The repository-only test mirror includes GNU's test-only uniq-u.c. GPL-3.0-only covers that source. The Hex artifact excludes the source and its license text. See LICENSE, GNU GPL 3.0, and NOTICE.

Complete codec catalog

The generator builds this table from the release inventories. Do not edit this section by hand. Codec names ignore ASCII letter case. You can use each alias to look up its codec. Aliases do not increase the canonical codec count.

112 canonical codecs Β· 334 aliases Β· 446 accepted names.

Registered codec names

Canonical codecAccepted aliasesStateful
ARMSCII-8β€”No
BIG5BIG-5
BIG-FIVE
BIGFIVE
CN-BIG5
CSBIG5
No
BIG5-HKSCSBIG5-HKSCS:2008
BIG5HKSCS
No
BIG5-HKSCS:1999β€”No
BIG5-HKSCS:2001β€”No
BIG5-HKSCS:2004β€”No
C99β€”No
CP1131IBM-1131No
CP1133IBM-CP1133No
CP1250IBM-1250
MS-EE
WINDOWS-1250
No
CP1251ANSI-1251
IBM-1251
MS-CYRL
WINDOWS-1251
No
CP1252IBM-1004
IBM-1252
MS-ANSI
WINDOWS-1252
No
CP1253IBM-1253
MS-GREEK
WINDOWS-1253
No
CP1254IBM-1254
MS-TURK
WINDOWS-1254
No
CP1255IBM-1255
MS-HEBR
WINDOWS-1255
No
CP1256IBM-1256
MS-ARAB
WINDOWS-1256
No
CP1257IBM-1257
WINBALTRIM
WINDOWS-1257
No
CP1258WINDOWS-1258No
CP850850
CSPC850MULTILINGUAL
IBM-4946
IBM-850
IBM850
No
CP862862
CSPC862LATINHEBREW
IBM-862
IBM862
No
CP866866
CSIBM866
IBM-866
IBM866
No
CP874IBM-874
WINDOWS-874
No
CP932IBM-932No
CP936MS936
WINDOWS-936
No
CP949IBM-949
KSC5601
UHC
No
CP950IBM-950No
EUC-CNCN-GB
CSGB2312
DECHANZI
EUCCN
GB2312
HP15CN
IBM-1381
IBM-1383
IBM-EUCCN
No
EUC-JPCSEUCPKDFMTJAPANESE
EUCJP
EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE
IBM-3372
IBM-33722
IBM-954
IBM-EUCJP
SDECKANJI
No
EUC-KR5601
CSEUCKR
DECKOREAN
EUCKR
IBM-970
IBM-EUCKR
No
EUC-TWCNS11643
CSEUCTW
EUCTW
EUCTW-1993
IBM-964
IBM-EUCTW
No
GB18030GB18030:2005No
GB18030:2022β€”No
GBKIBM-1386No
GB_1988-80CN
CSISO57GB1988
ISO-IR-57
ISO646-CN
No
GB_2312-80CHINESE
CSISO58GB231280
ISO-IR-58
No
GEORGIAN-ACADEMYβ€”No
GEORGIAN-PSβ€”No
HP-ROMAN8CSHPROMAN8
R8
ROMAN8
No
HZHZ-GB-2312Yes
ISO-2022-CNCSISO2022CNYes
ISO-2022-CN-EXTβ€”Yes
ISO-2022-JPCSISO2022JPYes
ISO-2022-JP-1CSJISENCODING
IBM-5054
JIS_ENCODING
Yes
ISO-2022-JP-2CSISO2022JP2Yes
ISO-2022-JP-MSCP50221Yes
ISO-2022-KRCSISO2022KRYes
ISO-8859-1CP819
CSISOLATIN1
IBM-819
IBM819
ISO-IR-100
ISO8859-1
ISO88591
ISO_8859-1
ISO_8859-1:1987
L1
LATIN1
No
ISO-8859-10CSISOLATIN6
ISO-IR-157
ISO8859-10
ISO_8859-10
ISO_8859-10:1992
L6
LATIN6
No
ISO-8859-11ISO8859-11
ISO_8859-11
No
ISO-8859-13IBM-921
ISO-IR-179
ISO8859-13
ISO_8859-13
L7
LATIN7
No
ISO-8859-14ISO-CELTIC
ISO-IR-199
ISO8859-14
ISO_8859-14
ISO_8859-14:1998
L8
LATIN8
No
ISO-8859-15IBM-923
ISO-IR-203
ISO8859-15
ISO885915
ISO_8859-15
ISO_8859-15:1998
LATIN-9
No
ISO-8859-16ISO-IR-226
ISO8859-16
ISO_8859-16
ISO_8859-16:2001
L10
LATIN10
No
ISO-8859-2CSISOLATIN2
IBM-912
ISO-IR-101
ISO8859-2
ISO88592
ISO_8859-2
ISO_8859-2:1987
L2
LATIN2
No
ISO-8859-3CSISOLATIN3
IBM-913
ISO-IR-109
ISO8859-3
ISO_8859-3
ISO_8859-3:1988
L3
LATIN3
No
ISO-8859-4CSISOLATIN4
IBM-914
ISO-IR-110
ISO8859-4
ISO_8859-4
ISO_8859-4:1988
L4
LATIN4
No
ISO-8859-5CSISOLATINCYRILLIC
CYRILLIC
IBM-915
ISO-IR-144
ISO8859-5
ISO88595
ISO_8859-5
ISO_8859-5:1988
No
ISO-8859-6ARABIC
ASMO-708
CSISOLATINARABIC
ECMA-114
IBM-1089
ISO-8859-6-E
ISO-8859-6-I
ISO-IR-127
ISO8859-6
ISO88596
ISO_8859-6
ISO_8859-6:1987
No
ISO-8859-7CSISOLATINGREEK
ECMA-118
ELOT_928
GREEK
GREEK8
IBM-813
ISO-IR-126
ISO-IR-227
ISO8859-7
ISO88597
ISO_8859-7
ISO_8859-7:1987
ISO_8859-7:2003
No
ISO-8859-8CSISOLATINHEBREW
ECMA-121
HEBREW
IBM-916
ISO-8859-8-E
ISO-8859-8-I
ISO-IR-138
ISO8859-8
ISO88598
ISO_8859-8
ISO_8859-8:1988
No
ISO-8859-9CSISOLATIN5
IBM-920
ISO-IR-148
ISO8859-9
ISO88599
ISO_8859-9
ISO_8859-9:1989
L5
LATIN5
No
ISO-IR-165CN-GB-ISOIR165No
JAVAβ€”No
JIS_C6220-1969-ROCSISO14JISC6220RO
ISO-IR-14
ISO646-JP
JP
No
JIS_X0201CSHALFWIDTHKATAKANA
JISX0201-1976
X0201
No
JIS_X0208CSISO87JISX0208
ISO-IR-168
ISO-IR-87
JIS0208
JIS_C6226-1983
JIS_X0208-1983
JIS_X0208-1990
X0208
No
JIS_X0212CSISO159JISX02121990
ISO-IR-159
JIS_X0212-1990
JIS_X0212.1990-0
X0212
No
JOHABCP1361
KO_KR.JOHAP92
No
KOI8-RCSKOI8R
IBM-878
No
KOI8-RUβ€”No
KOI8-Tβ€”No
KOI8-Uβ€”No
KSC_5601CSKSC56011987
ISO-IR-149
KOREAN
KS_C_5601-1987
KS_C_5601-1989
No
MacArabicβ€”No
MacCentralEuropeβ€”No
MacCroatianβ€”No
MacCyrillicβ€”No
MacGreekβ€”No
MacHebrewβ€”No
MacIcelandβ€”No
MacRomanCSMACINTOSH
MAC
MACINTOSH
No
MacRomaniaβ€”No
MacThaiβ€”No
MacTurkishβ€”No
MacUkraineβ€”No
MULELAO-1β€”No
NEXTSTEPβ€”No
PT154CP154
CSPTCP154
CYRILLIC-ASIAN
PTCP154
No
RK1048CSKZ1048
KZ-1048
STRK1048-2002
No
SHIFT_JISCSSHIFTJIS
MS_KANJI
PCK
SHIFT-JIS
SJIS
No
TCVNTCVN-5712
TCVN5712-1
TCVN5712-1:1993
No
TIS-620ISO-IR-166
TACTIS
TIS620
TIS620-0
TIS620.2529-1
TIS620.2533
TIS620.2533-0
TIS620.2533-1
No
UCS-2CSUNICODE
ISO-10646-UCS-2
No
UCS-2-INTERNALβ€”No
UCS-2-SWAPPEDβ€”No
UCS-2BECSUNICODE11
ISO-IR-162
ISO-IR-174
ISO-IR-176
UNICODE-1-1
UNICODEBIG
No
UCS-2LEUNICODELITTLENo
UCS-4CSUCS4
ISO-10646-UCS-4
No
UCS-4-INTERNALβ€”No
UCS-4-SWAPPEDβ€”No
UCS-4BEISO-IR-163
ISO-IR-175
ISO-IR-177
No
UCS-4LEβ€”No
US-ASCII646
ANSI_X3.4-1968
ANSI_X3.4-1986
ASCII
CP367
CSASCII
IBM-367
IBM367
ISO-IR-6
ISO646-US
ISO_646.IRV:1991
US
No
UTF-16ISO-IR-193
ISO-IR-194
ISO-IR-195
No
UTF-16BECP1201
UNICODEFFFE
WINDOWS-1201
No
UTF-16LECP1200
WINDOWS-1200
No
UTF-32β€”No
UTF-32BEβ€”No
UTF-32LECP12000
WINDOWS-12000
No
UTF-7CSUNICODE11UTF7
UNICODE-1-1-UTF-7
Yes
UTF-8IBM-1208
ISO-IR-190
ISO-IR-191
ISO-IR-192
ISO-IR-196
UTF8
No
VISCIICSVISCII
VISCII1.1-1
No