Last active
June 5, 2024 13:39
-
-
Save iagobelo/3fa4626f3c09e2d10693ba89a5c270ee to your computer and use it in GitHub Desktop.
Base64 encoder and decoder written with types only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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