Bodu.Text.Encoding Namespace
- Package
-
Bodu.Text.Encoding 1.0.0
Purpose
Bodu.Text.Encoding is a focused, allocation-conscious library of binary-to-text encodings. The five core radix encodings .NET applications reach for - Base16, Base32, Base64, Base58, Base85 - each carry the same modern API shape: span- and UTF-8-friendly overloads, OperationStatus-returning streaming methods, length-prediction helpers, validation predicates, and the unified IBinaryEncoding interface that lets code select an encoding at runtime. Alongside them sit three special-purpose encodings - Base45 (RFC 9285, for QR-code payloads), Base62 (GMP-style, for compact identifiers), and Bech32 / Bech32m (BIP 173 / 350, checksummed addresses with a human-readable part) - plus the convenience wrappers Base58Check and Base64Url.
The package fills two gaps that Convert and System.Buffers.Text.Base64 leave open: variants the BCL does not cover (base32hex, Crockford Base32, z-base-32, Base58 Bitcoin / Flickr / Ripple, Ascii85, Z85, Base45, Base62, Bech32 / Bech32m), and lenient parsing / formatting decoration - 0x prefix tolerance, whitespace stripping, byte spacing, line breaks every 64 / 76 characters - for the encodings that benefit from them.
For self-framing document formats (CSV / TSV, DotEnv, INI), see the companion Bodu.Text.Formats umbrella package - namespaces Bodu.Text.Delimited, Bodu.Text.DotEnv, and Bodu.Text.Ini. For System.Text.Json-style object mapping to TOML, Bencode, or YAML, see the Bodu.Text.Toml, Bodu.Text.Bencode, and Bodu.Text.Yaml serializers.
Static documentation
- Bodu.Text.Encoding introduction - namespaces, headline types, scenarios.
- Bodu.Text.Encoding core concepts - vocabulary: alphabet, variant, terminal quantum, padding, shortcut, decoration,
OperationStatus. - Bodu.Text.Encoding getting started - install and minimal samples for each encoding.
- Bodu.Text.Encoding guides - per-encoding deep dives plus the
IBinaryEncodingruntime-selection pattern.
Key types
Core radix encodings (Bodu.Text.Encoding)
- Base16 - hexadecimal; 4 bits per symbol; flexible formatting (case,
0xprefix, line breaks, byte spacing); lenient parsing. - Base32 - 5 bits per symbol; four variants via Base32Variant: Standard (RFC 4648 §6), HexExtended (RFC 4648 §7), Crockford, ZBase32; padding control.
- Base64 - 6 bits per symbol; three variants via Base64Variant: Standard (RFC 4648 §4), UrlSafe (RFC 4648 §5), Mime (RFC 2045 with 76-char wrap). Delegates inner conversion to the BCL for SIMD speed.
- Base58 - non-power-of-two radix using big-integer divmod; two variants via Base58Variant: BitcoinFlickr (default), Ripple. Preserves leading zeros.
- Base85 - 4-byte block → 5 chars; three variants via Base85Variant: Ascii85 (Adobe, with
zshortcut), Z85 (RFC 32 ZeroMQ; 4-byte alignment), GitCompact (Gitbase85.calphabet; compact self-delimiting tail plus theEncodeGitPadded/DecodeGitPaddedline primitive).
Special-purpose encodings
- Base45 - RFC 9285; the compact alphanumeric encoding carried inside a QR code's Alphanumeric mode. 45-character alphabet; no padding; not streamable.
- Base62 - GMP-style alphabet
0-9 A-Z a-z; big-integer divmod; leading zero bytes preserved as leading0characters. Suited to short URLs and compact identifiers. - Bech32 - BIP 173 (Bech32) and BIP 350 (Bech32m); a checksummed base-32 format comprising a human-readable part (HRP), the
1separator, a 5-bit data part, and a six-symbol error-detecting checksum. The Bech32Encoding enum selects the scheme and is reported on decode. - Base58Check - Base58 with the Bitcoin-style four-byte double-SHA-256 checksum appended on encode and verified on decode. The right entry point for address- and key-style payloads.
- Base64Url - the RFC 4648 §5 URL- and filename-safe Base64 as a first-class type (mirrors
System.Buffers.Text.Base64Url), with padding omitted by default and a UTF-8 byte path.
Escape-based encodings
These escape a subset of octets (=HH / %HH) while passing most printable ASCII through literally, so their output is content-dependent and mode-driven. They are intentionally not IBinaryEncoding members.
- QuotedPrintable - MIME Quoted-Printable body encoding (RFC 2045 §6.7) with QuotedPrintableEncodingMode (Binary / Text) and QuotedPrintableEncodingOptions (line length, newline); strict-by-default decoding with opt-in lowercase-hex, bare-LF, and trailing-whitespace relaxations via QuotedPrintableDecodingOptions. Not RFC 2047
Qheader encoding; not a MIME message parser. - PercentEncoding - URI / form percent-encoding (RFC 3986 §2.1 plus the WHATWG
application/x-www-form-urlencodedrules) with PercentEncodingMode (UriComponent / PathSegment / Query / FormUrlEncoded) and the EncodeString / DecodeString text helpers; uppercase hex emit, both-case accept, opt-in relaxed literals via PercentDecodingOptions. Not a URL parser.
Runtime selection
- IBinaryEncoding - unified contract for runtime-pluggable encoding choice.
Encode,Decode,GetMaxEncodedLength,GetMaxDecodedLength,IsValid,TryEncode,TryDecode, plusNameandDescription. - BinaryEncodings - pre-configured singleton instances (
Base16Lower,Base16Upper,Base32,Base32Hex,Base32Crockford,Base32ZBase32,Base45,Base58,Base58Ripple,Base62,Base64,Base64Mime,Base64UrlSafe,Ascii85,Base85Git,Z85) plus theGet(name)lookup for configuration-driven selection. (Bech32 and Base58Check require an HRP / checksum, and the escape-based Quoted-Printable / percent-encoding carry mode information, so none are surfaced throughIBinaryEncoding.) - BinaryEncodingExtensions - fluent extension methods on
byte[],ReadOnlySpan<byte>, andstring(ToBase16String,FromBase64String,ToBase64UrlString,Encode(IBinaryEncoding), …).
Shared option types
- BaseFormattingOptions - encode-side flags:
UpperCase,InsertLineBreaks,IncludePrefix,InsertSpacing,OmitPadding. - BaseFormatStyles - decode-side flags:
AllowPrefix,IgnoreWhitespace,AllowMissingPadding.
Example
using Bodu.Text.Encoding;
using System.Buffers;
using System.Security.Cryptography;
// --- Base16: print a hash digest as a formatted hex dump ----------------------
byte[] digest = SHA256.HashData("hello"u8.ToArray());
string dump = Base16.Encode(digest,
BaseFormattingOptions.UpperCase
| BaseFormattingOptions.InsertSpacing
| BaseFormattingOptions.IncludePrefix);
// dump → "0x2C F2 4D BA ..."
// --- Base64 URL-safe: decode a JWT segment with no padding -------------------
byte[] headerBytes = Base64.Decode(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
Base64Variant.UrlSafe,
BaseFormatStyles.AllowMissingPadding);
// --- Base32 Standard: TOTP secret for a user --------------------------------
byte[] secret = RandomNumberGenerator.GetBytes(20);
string display = Base32.Encode(secret, Base32Variant.Standard, BaseFormattingOptions.OmitPadding);
// --- Base58 BitcoinFlickr: decode a mainnet P2PKH address -------------------
byte[] payload = Base58.Decode("1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L");
// --- Base85 Ascii85 with the z shortcut -------------------------------------
string ascii85 = Base85.Encode(new byte[] { 0, 0, 0, 0 }, Base85Variant.Ascii85);
// ascii85 → "z"
// --- Base85 Git: compact, self-delimiting binary-patch alphabet -------------
string gitB85 = Base85.Encode("hello"u8.ToArray(), Base85Variant.GitCompact); // → "Xk~0{Zv"
// --- Quoted-Printable: MIME message body ------------------------------------
string qp = QuotedPrintable.Encode("café = møney"u8.ToArray()); // printable + =HH escapes
byte[] qpBack = QuotedPrintable.Decode(qp);
// --- Percent-encoding: URI component and form field -------------------------
string component = PercentEncoding.EncodeString("a/b?c=d"); // → "a%2Fb%3Fc%3Dd"
string formField = PercentEncoding.EncodeString("a b+c", mode: PercentEncodingMode.FormUrlEncoded); // → "a+b%2Bc"
// --- Base45: encode a payload for a QR code (RFC 9285) -----------------------
string qrPayload = Base45.Encode("AB"u8.ToArray()); // → "BB8"
// --- Base62: compact identifier from random bytes ---------------------------
string shortId = Base62.Encode(RandomNumberGenerator.GetBytes(8));
// --- Bech32m: encode raw bytes under a human-readable part ------------------
byte[] program = Base16.Decode("751e76e8199196d454941c45d1b3a323f1433bd6");
string addr = Bech32.EncodeFromBytes("bc", program, Bech32Encoding.Bech32m); // 8-bit → 5-bit groups
Bech32.DecodeToBytes(addr, out string hrp, out byte[] data, out Bech32Encoding scheme);
// hrp → "bc"; data → program; scheme → Bech32Encoding.Bech32m
// --- Base58Check: checksum-protected payload --------------------------------
string wif = Base58Check.Encode(secret);
byte[] recovered = Base58Check.Decode(wif); // verifies, then strips checksum
// --- Runtime selection via IBinaryEncoding ----------------------------------
IBinaryEncoding encoding = BinaryEncodings.Get("base64-urlsafe");
string token = encoding.Encode(secret);
byte[] back = encoding.Decode(token);
// --- Streaming UTF-8 decode with OperationStatus ----------------------------
OperationStatus status = Base16.DecodeFromUtf8(
utf8Source,
outputBuffer,
out int consumed,
out int written,
BaseFormatStyles.None,
isFinalBlock: true);
Notes
- ASCII alphabets / UTF-8 byte path. Every variant's alphabet is pure ASCII, so the UTF-8 byte form is bit-identical to the character form. The
EncodeToUtf8/DecodeFromUtf8overloads are the natural choice when bytes come from a network or file pipeline - they avoid the allocation of an intermediatestringorchar[]. OperationStatusand streaming. Base16, Base32, and Base64 expose theSystem.Buffers-styleOperationStatusreturn convention used bySystem.Buffers.Text.Base64:Done,DestinationTooSmall,InvalidData, and (streaming only,isFinalBlock: false)NeedMoreData. This makes them safe to drop into chunked stream pipelines without buffering the entire input.- Base58 and Base85 are not streamable. Base58 needs the entire input for its big-integer divmod; Base85 needs fixed-size block packing. Both surfaces accept
isFinalBlockfor API consistency but ignore the flag - pass the entire input as a single span. - The special-purpose encodings are single-shot. Base45, Base62, and Bech32 each require the whole input at once (Base45 packs in two/three-character groups, Base62 uses big-integer divmod, Bech32 must compute a checksum over the entire data part), so they expose no
OperationStatusstreaming path. They throwFormatExceptionon invalid input and offerTry*variants that report failure asfalse. - Bech32 is HRP-aware and checksum-verified. Bech32 encodes and decodes the whole string - human-readable part,
1separator, data, and checksum - rather than a flat byte buffer, which is why it sits outside IBinaryEncoding. Decode reports which scheme (Bech32 vs Bech32m) validated the checksum through an out parameter; the BIP 173 90-character limit is enforced on decode, andConvertBitsconverts between 8-bit bytes and 5-bit groups. Base58Check likewise verifies its appended four-byte double-SHA-256 checksum on decode and throws on a corrupted string. - Stateless encodings. Every encoding type is a
static classwith no instance state. There is no lookup-table cache to share across instances (encodings are stateless), no thread-affinity, and no global configuration. The runtime-selection types in BinaryEncodings are pre-configured singletons over the same static APIs. - Decoder strictness. Decoders are strict by default - only the canonical alphabet, padding, and quantum length are accepted - and lenient when one or more BaseFormatStyles flags are set:
AllowPrefixaccepts a0x/0Xprefix;IgnoreWhitespacestrips ASCII space, tab, CR, LF anywhere;AllowMissingPaddingaccepts inputs without trailing=.DecodethrowsFormatExceptionon validation failure;TryDecodereturnsfalse. - Determinism and portability. Encoders are deterministic - given a byte sequence and an option set they always produce the same canonical output across platforms and architectures, including the BCL-delegated Base64 path.
- No coupling to other Bodu packages at the consumer surface. The only dependency is
Bodu.Corefor shared throw helpers. The package has no external NuGet references. - Escape-based encodings are not
IBinaryEncodingmembers. QuotedPrintable and PercentEncoding escape only a subset of octets and select behaviour through a mode / options object, so their output length is content-dependent and the parameterless IBinaryEncoding contract cannot represent them. Like the radix encodings they are statelessstatic classes with span-firstTry*methods that never throw for malformed input or an undersized destination, throwingEncode/Decodewrappers, and deterministic length helpers. Quoted-Printable guarantees round-trip-safe output (it never emits decoder-rejected trailing whitespace); percent-encoding always emits uppercase hex and accepts both cases on decode. - See also: the introduction, core concepts, and getting-started; the per-encoding guides under Bodu.Text.Encoding guides - including Quoted-Printable and percent-encoding; and the
IBinaryEncodinginterface for runtime selection.
Classes
- Base16
Provides Base16 (hexadecimal) encoding and decoding of binary data, with support for flexible output formatting (case selection, byte spacing, line wrapping,
0xprefix) and lenient input parsing (prefix tolerance, whitespace stripping).
- Base32
Provides Base32 encoding and decoding of binary data across multiple variants (RFC 4648 standard and hex-extended, Crockford, z-base-32), with optional padding control and whitespace tolerance during parsing.
- Base45
Provides Base45 encoding and decoding of binary data as defined by RFC 9285 - the compact alphanumeric encoding used to carry binary payloads inside a QR code's Alphanumeric mode.
- Base58
Provides Base58 encoding and decoding of binary data using the Bitcoin/Flickr or Ripple alphabets.
- Base58Check
Provides Base58Check encoding and decoding - a Base58 superset used by Bitcoin addresses, WIF private keys, and related protocols. The encoder appends a four-byte checksum derived from the leading bytes of
SHA-256(SHA-256(payload))so that mistyped or truncated input can be detected at decode time.
- Base62
Provides Base62 encoding and decoding of binary data using the GMP-style alphabet
0-9 A-Z a-z.
- Base64
Provides Base64 encoding and decoding of binary data across the RFC 4648 standard, URL-safe, and MIME variants, with optional padding control and whitespace tolerance during parsing.
- Base64Url
Provides the RFC 4648 §5 URL- and filename-safe Base64 encoding as a first-class type, mirroring the shape of
System.Buffers.Text.Base64Urlintroduced in .NET 9. URL-safe Base64 swaps+and/for-and_, and conventionally omits trailing=padding (matching JWT and OAuth practice).
- Base85
Provides Base85 encoding and decoding of binary data using the Adobe Ascii85, ZeroMQ Z85, or Git-style alphabets.
- Bech32
Provides Bech32 (BIP-173) and Bech32m (BIP-350) encoding and decoding - a checksummed base-32 format consisting of a human-readable part (HRP), the
1separator, a data part of 5-bit groups, and a six-symbol error-detecting checksum.
- BinaryEncodingExtensions
Provides fluent extension methods on byte arrays, ReadOnlySpan<T> of byte and char, and string that route through the canonical encoding implementations.
- BinaryEncodings
Provides ready-made IBinaryEncoding instances for every variant supported by the library.
- PercentEncoding
Provides span-first percent-encoding and decoding of URI components and form-style values (RFC 3986 §2.1 with the WHATWG
application/x-www-form-urlencodedrules), selecting the unescaped character set by component mode.
- QuotedPrintable
Provides MIME Quoted-Printable body encoding and decoding (RFC 2045 §6.7) of binary data, with selectable binary or canonical-text line handling, configurable soft line wrapping, and strict-by-default decoding.
Structs
- QuotedPrintableEncodingOptions
Configures how QuotedPrintable produces Quoted-Printable output: the line-break treatment, the soft line-wrap column, and the newline sequence.
Interfaces
- IBinaryEncoding
Represents a binary-to-text encoding scheme (Base16, Base32, Base64, Base58, Base85, …) with the variant pre-bound, so that callers can pick or inject an encoding at runtime instead of dispatching through static classes.
Enums
- Base16Variant
Identifies the hexadecimal alphabet case used by Base16.
- Base32Variant
Identifies the Base32 alphabet and decoding rules used by Base32.
- Base58Variant
Identifies the Base58 alphabet used by Base58.
- Base64Variant
Identifies the Base64 alphabet and formatting rules used by Base64.
- Base85Variant
Identifies the Base85 alphabet and framing rules used by Base85.
- BaseFormatStyles
Specifies formatting styles that influence how base-encoded input strings are parsed during decoding.
- BaseFormattingOptions
Defines formatting options that influence how encoded output is generated from binary data using any positional numeral system.
- Bech32Encoding
Identifies the checksum constant used by Bech32 - the original Bech32 scheme defined by BIP-173 or the Bech32m revision defined by BIP-350.
- PercentDecodingOptions
Specifies relaxations applied by the PercentEncoding decoder over its strict RFC 3986 default.
- PercentEncodingMode
Identifies the set of characters PercentEncoding leaves unescaped, selecting the URI component or form-encoding rules to apply.
- QuotedPrintableDecodingOptions
Specifies relaxations applied by the QuotedPrintable decoder over its strict RFC 2045 default.
- QuotedPrintableEncodingMode
Identifies how QuotedPrintable treats line breaks in the source when encoding.