-
Maps characters (text) to byte sequences and vice versa. Used for text fields in serialization.
Naming Confusion
-
When someone says "custom package encoding" , they usually mean:
-
A framing protocol (how message start/end is delimited).
-
A custom serialization/deserialization strategy.
-
A binary or textual format for transmitting structures over the network.
-
-
Using "encoding" for package framing strategies is technically valid but potentially ambiguous.
-
In networking, itβs better to use more specific terms.
-
The word "encoding" itself isnβt wrong but should be interpreted in the technical context.
Text
Each solves a different constraint:
ASCII β simplicity (pre-Unicode world)
UTF-8 β compatibility + compactness
UTF-16 β compromise for legacy 16-bit systems
UTF-32 β simplicity of indexing
if it weren't for grapheme, array of unicode would be a viable choice?
Yesβif grapheme clusters did not exist, then βarray of Unicode code pointsβ ([]u32) would be a much cleaner and more viable model. In fact, it would be close to an idealized design.
but: every thing is 4 bytes; which is very bad.
Unicode
-
Unicode vs Encoding :
-
Unicode defines characters and assigns them numbers.
-
Encodings define how those numbers are stored as bytes.
-
Computers store bytes, not code points.
-
An encoding converts: Code Point <-> Bytes.
-
The major Unicode encodings are:
-
ASCII
-
UTF-8
-
UTF-16
-
UTF-32
-
-
-
A code point is just an integer.
-
It uses values from
U+0000 (0) -> U+10FFFF (1_114_111), which fits in 21 bits.
| Character | Code Point | Decimal |
|-----------|------------|---------|
|A| U+0041 | 65 |
|Γ©| U+00E9 | 233 |
|δΈ| U+4E2D | 20013 |
|π| U+1F600 | 128512 |
ASCII (American Standard Code for Information Interchange)
-
ASCII is simpler because it's both a character set and an encoding.
-
Uses exactly 1 byte (8 bits) per character.
-
Only defines
0x00 (0) -> 0x7F (127)
| Character | Code Point | Hex Byte |
|-----------|------------|-------------------|
|A| U+0041 | 41 |
|Γ©| U+00E9 | not representable |
|δΈ| U+4E2D | not representable |
|π| U+1F600 | not representable | -
The problem :
-
Too few representations.
-
UTF-8
-
Variable-width encoding.
-
1 byte for ASCII
-
2 bytes for some European characters
-
3 bytes for most world scripts
-
4 bytes for emoji and other supplementary characters
| Character | Code Point | UTF-8 Bytes |
|-----------|------------|-------------|
|A| U+0041 | 41 |
|Γ©| U+00E9 | C3 A9 |
|δΈ| U+4E2D | E4 B8 AD |
|π| U+1F600 | F0 9F 98 80 |
-
-
The problem :
-
For chars: UTF-8 only answers "How do I store text as bytes?", but it does not answer "What is a character?".
-
If your language says
type char = u8, then what is 'π'? It cannot fit.
-
-
For strings: they are no longer indexable in O(1) by character
-
UTF-16
-
Stores text in 16-bit units.
-
Size :
-
BMP characters (Basic Multilingual Plane, U+0000 to U+FFFF) use 2 bytes
-
Characters outside BMP (e.g., emojis, historical scripts) use 4 bytes (surrogate pairs)
-
More efficient for languages with many BMP characters (e.g., many Asian languages)
| Character | Code Point | UTF-16 Bytes |
|-----------|------------|--------------|
|A| U+0041 | 0041 |
|Γ©| U+00E9 | 00E9 |
|δΈ| U+4E2D | 4E2D |
|π| U+1F600 | D83D DE00 |
-
-
The emoji requires two 16-bit units; Those paired values are called a surrogate pair.
UTF-32
-
Stores every code point as a 32-bit integer.
-
Very simple.
-
Very memory-heavy.
| Character | Code Point | UTF-32 Bytes |
|-----------|------------|--------------|
|A| U+0041 | 00000041 |
|Γ©| U+00E9 | 000000E9 |
|δΈ| U+4E2D | 00004E2D |
|π| U+1F600 | 0001F600 |
Example
s := "hello π world γγγ«γ‘γ―"
-
Total of bytes: 32 .
-
hello: 5 -
(space): 1 -
π: 4 -
(space): 1 -
world: 5 -
(space): 1 -
γγγ«γ‘γ―: 15 (5 Γ 3)
-
-
Total of codepoints: 19 .
-
hello: 5 -
(space): 1 -
πβ 1 -
(space): 1 -
worldβ 5 -
(space): 1 -
γγγ«γ‘γ―β 5
-
-
Overview:
| Character | UTF-8 bytes (hex) | Decimal |
| --------- | ----------------- | ------------------ |
|h|68| 104 |
|e|65| 101 |
|l|6C| 108 |
|l|6C| 108 |
|o|6F| 111 |
| space |20| 32 |
|π|F0 9F 98 83| 240, 159, 152, 131 |
| space |20| 32 |
|w|77| 119 |
|o|6F| 111 |
|r|72| 114 |
|l|6C| 108 |
|d|64| 100 |
| space |20| 32 |
|γ|E3 81 93| 227, 129, 147 |
|γ|E3 82 93| 227, 130, 147 |
|γ«|E3 81 AB| 227, 129, 171 |
|γ‘|E3 81 A1| 227, 129, 161 |
|γ―|E3 81 AF| 227, 129, 175 |
Base64
-
Is a way to represent arbitrary binary data using only printable ASCII characters.
-
It is not encryption or compressionβjust an encoding so binary data can be stored safely in text formats.
-
It is called Base64 because the encoding uses a numeral system with 64 distinct symbols to represent data.
-
Each Base64 character encodes 6 bits.
-
So you need exactly 64 symbols (
2^6 = 64) to represent every possible 6-bit value.
-
-
Base64 exists as many systems historically handled text only.
-
Raw binary can contain:
-
null bytes (0x00)
-
control characters
-
non-printable bytes
-
-
Converts binary β safe text using only:
AβZ aβz 0β9 + / -
Padding uses
=but it is not part of the base.
Size
-
Base64 increases size by about 33%.
-
4 output bytes per 3 input bytes.
encoded_size β ceil(input_size / 3) * 4
-
"Why on earth would you use an encoding that increases the size of the thing?"
-
Because Base64 solves transport and compatibility problems, not size efficiency. It is used when binary must safely travel through systems that are text-only or text-fragile.
-
Raw binary can break many pipelines due to:
-
null bytes (0x00)
-
control characters
-
encoding assumptions (UTF-8/UTF-16)
-
line-ending conversions
-
legacy text parsers
-
-
Historically (and still today), many formats and tools expect text, not arbitrary bytes.
-
Base64 guarantees the data contains only safe printable ASCII.
-
Core idea
-
Base64 works in 6-bit chunks.
-
Binary bytes are 8 bits each
-
Base64 symbols encode 6 bits each
-
So it repacks data
3 bytes (24 bits) β 4 Base64 characters
-
as
3 Γ 8 = 24 bits
4 Γ 6 = 24 bits
-
Example :
-
Input:
"Man" -
Write bytes:
M = 01001101 a = 01100001 n = 01101110 010011010110000101101110 -
Split into 6-bit groups:
010011 010110 000101 101110 -
In decimals:
19 22 5 46 -
Map to Base64 alphabet:
0β25 β AβZ 26β51 β aβz 52β61 β 0β9 62 β + 63 β / -
Output:
19 β T 22 β W 5 β F 46 β u-
Concatenated:
TWFu -
-
Padding rules
-
If input length is not divisible by 3, Base64 pads with
=. -
Example :
-
Input:
"Ma" -
Write binary:
01001101 01100001 -
Split into 6-bit groups:
010011 010110 000100 000000 -
In decimals -> Map to Base64 alphabet.
-
Output:
TWE=
-