Skip to content

Instantly share code, notes, and snippets.

@iagobelo
Last active June 5, 2024 13:39
Show Gist options
  • Save iagobelo/3fa4626f3c09e2d10693ba89a5c270ee to your computer and use it in GitHub Desktop.
Save iagobelo/3fa4626f3c09e2d10693ba89a5c270ee to your computer and use it in GitHub Desktop.
Base64 encoder and decoder written with types only.
/**
* by @iagobelo
* Decoder in progress!!!
*/
type STRING_TO_CONVERT = "xablau";
type BitResult = Base64EncodeWithoutBa64Chars<STRING_TO_CONVERT>; // 011110-000110-000101-100010-011011-000110-000101-110101
type Result = Base64Encode<STRING_TO_CONVERT>; // eGFibGF1
/**
* Map where the key is an ASCII char and the value
* is a tuple of string bit pairs (four pairs with 2bits each)
* that represents the char.
*/
// prettier-ignore
type AsciiTable = {
[asciiChar: string]: [bitPair: string, bitPair: string, bitPair: string, bitPair: string]
' ': ['00', '10', '00', '00'];
'!': ['00', '10', '00', '01']; '"': ['00', '10', '00', '10']; '#': ['00', '10', '00', '11'];
'$': ['00', '10', '01', '00']; '%': ['00', '10', '01', '01']; '&': ['00', '10', '01', '10'];
'\'': ['00', '10', '01', '11']; '(': ['00', '10', '10', '00']; ')': ['00', '10', '10', '01'];
'*': ['00', '10', '10', '10']; '+': ['00', '10', '10', '11']; ',': ['00', '10', '11', '00'];
'-': ['00', '10', '11', '01']; '.': ['00', '10', '11', '10']; '/': ['00', '10', '11', '11'];
'0': ['00', '11', '00', '00']; '1': ['00', '11', '00', '01']; '2': ['00', '11', '00', '10'];
'3': ['00', '11', '00', '11']; '4': ['00', '11', '01', '00']; '5': ['00', '11', '01', '01'];
'6': ['00', '11', '01', '10']; '7': ['00', '11', '01', '11']; '8': ['00', '11', '10', '00'];
'9': ['00', '11', '10', '01']; ':': ['00', '11', '10', '10']; ';': ['00', '11', '10', '11'];
'<': ['00', '11', '11', '00']; '=': ['00', '11', '11', '01']; '>': ['00', '11', '11', '10'];
'?': ['00', '11', '11', '11']; '@': ['01', '00', '00', '00']; 'A': ['01', '00', '00', '01'];
'B': ['01', '00', '00', '10']; 'C': ['01', '00', '00', '11']; 'D': ['01', '00', '01', '00'];
'E': ['01', '00', '01', '01']; 'F': ['01', '00', '01', '10']; 'G': ['01', '00', '01', '11'];
'H': ['01', '00', '10', '00']; 'I': ['01', '00', '10', '01']; 'J': ['01', '00', '10', '10'];
'K': ['01', '00', '10', '11']; 'L': ['01', '00', '11', '00']; 'M': ['01', '00', '11', '01'];
'N': ['01', '00', '11', '10']; 'O': ['01', '00', '11', '11']; 'P': ['01', '01', '00', '00'];
'Q': ['01', '01', '00', '01']; 'R': ['01', '01', '00', '10']; 'S': ['01', '01', '00', '11'];
'T': ['01', '01', '01', '00']; 'U': ['01', '01', '01', '01']; 'V': ['01', '01', '01', '10'];
'W': ['01', '01', '01', '11']; 'X': ['01', '01', '10', '00']; 'Y': ['01', '01', '10', '01'];
'Z': ['01', '01', '10', '10']; '[': ['01', '01', '10', '11']; '\\': ['01', '01', '11', '00'];
']': ['01', '01', '11', '01']; '^': ['01', '01', '11', '10']; '_': ['01', '01', '11', '11'];
'`': ['01', '10', '00', '00']; 'a': ['01', '10', '00', '01']; 'b': ['01', '10', '00', '10'];
'c': ['01', '10', '00', '11']; 'd': ['01', '10', '01', '00']; 'e': ['01', '10', '01', '01'];
'f': ['01', '10', '01', '10']; 'g': ['01', '10', '01', '11']; 'h': ['01', '10', '10', '00'];
'i': ['01', '10', '10', '01']; 'j': ['01', '10', '10', '10']; 'k': ['01', '10', '10', '11'];
'l': ['01', '10', '11', '00']; 'm': ['01', '10', '11', '01']; 'n': ['01', '10', '11', '10'];
'o': ['01', '10', '11', '11']; 'p': ['01', '11', '00', '00']; 'q': ['01', '11', '00', '01'];
'r': ['01', '11', '00', '10']; 's': ['01', '11', '00', '11']; 't': ['01', '11', '01', '00'];
'u': ['01', '11', '01', '01']; 'v': ['01', '11', '01', '10']; 'w': ['01', '11', '01', '11'];
'x': ['01', '11', '10', '00']; 'y': ['01', '11', '10', '01']; 'z': ['01', '11', '10', '10'];
'{': ['01', '11', '10', '11']; '|': ['01', '11', '11', '00']; '}': ['01', '11', '11', '01'];
'~': ['01', '11', '11', '10']; '': ['01', '11', '11', '11'];
};
/**
* Map where the key is a 6bit string representation of a valid
* Base64 char and the value is the char itself.
*/
// prettier-ignore
type Base64Chars = {
[key: string]: string
'000000': 'A'; '000001': 'B'; '000010': 'C'; '000011': 'D'; '000100': 'E';
'000101': 'F'; '000110': 'G'; '000111': 'H'; '001000': 'I'; '001001': 'J';
'001010': 'K'; '001011': 'L'; '001100': 'M'; '001101': 'N'; '001110': 'O';
'001111': 'P'; '010000': 'Q'; '010001': 'R'; '010010': 'S'; '010011': 'T';
'010100': 'U'; '010101': 'V'; '010110': 'W'; '010111': 'X'; '011000': 'Y';
'011001': 'Z'; '011010': 'a'; '011011': 'b'; '011100': 'c'; '011101': 'd';
'011110': 'e'; '011111': 'f'; '100000': 'g'; '100001': 'h'; '100010': 'i';
'100011': 'j'; '100100': 'k'; '100101': 'l'; '100110': 'm'; '100111': 'n';
'101000': 'o'; '101001': 'p'; '101010': 'q'; '101011': 'r'; '101100': 's';
'101101': 't'; '101110': 'u'; '101111': 'v'; '110000': 'w'; '110001': 'x';
'110010': 'y'; '110011': 'z'; '110100': '0'; '110101': '1'; '110110': '2';
'110111': '3'; '111000': '4'; '111001': '5'; '111010': '6'; '111011': '7';
'111100': '8'; '111101': '9'; '111110': '+'; '111111': '/';
}
/**
* Iterates over the given string and returns an array of characters
* splitted by the separetor.
*/
type Split<
S extends string,
Separator extends string = ""
> = S extends `${infer T}${Separator}${infer U}`
? U extends ""
? [S]
: [T, ...Split<U, Separator>]
: [S];
/**
* Iterates over the array of chars then get the 8 bit representation of each
* char and finally transform the 8-bit chars into 6-bit base64 chars.
*/
// prettier-ignore
type FromCharArrayTo6BitBinaryArray<A extends string[]> =
// Has 3 or more chars left
A extends [infer C1, infer C2, infer C3, ...infer Rest]
? `${AsciiTable[A[0]][0]}${AsciiTable[A[0]][1]}${AsciiTable[A[0]][2]}-${
AsciiTable[A[0]][3]}${AsciiTable[A[1]][0]}${AsciiTable[A[1]][1]}-${
AsciiTable[A[1]][2]}${AsciiTable[A[1]][3]}${AsciiTable[A[2]][0]}-${
AsciiTable[A[2]][1]}${AsciiTable[A[2]][2]}${AsciiTable[A[2]][3]}${
Rest extends string[] ? `${Rest extends [infer D1, ...infer DRest] ? `-${FromCharArrayTo6BitBinaryArray<Rest>}` : ''}` : ''}` :
// Has 2 chars left
A extends [infer C1, infer C2, ...infer Rest]
? `${AsciiTable[A[0]][0]}${AsciiTable[A[0]][1]}${AsciiTable[A[0]][2]}-${
AsciiTable[A[0]][3]}${AsciiTable[A[1]][0]}${AsciiTable[A[1]][1]}-${
AsciiTable[A[1]][2]}${AsciiTable[A[1]][3]}00` :
// Has 1 chart left
A extends [infer C1, ...infer Rest]
? `${AsciiTable[A[0]][0]}${AsciiTable[A[0]][1]}${AsciiTable[A[0]][2]}-${AsciiTable[A[0]][3]}0000`
: '';
/**
* Iterates over the array of 6bit string group and gets the Base64
* representation of each char.
*/
type FromBinaryArrayToBase64Chars<A extends string[]> = A extends [
infer C1,
...infer Rest
]
? `${Base64Chars[A[0]]}${Rest extends string[]
? FromBinaryArrayToBase64Chars<Rest>
: Rest extends `${infer A}${infer B}${infer C}${infer D}00`
? "="
: Rest extends `${infer A}${infer B}0000`
? "=="
: ""}`
: "";
type Base64Encode<S extends string> = FromBinaryArrayToBase64Chars<
Split<FromCharArrayTo6BitBinaryArray<Split<S>>, "-">
>;
type Base64EncodeWithoutBa64Chars<S extends string> =
FromCharArrayTo6BitBinaryArray<Split<S>>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment