Last active
May 21, 2024 19:37
-
-
Save liath/f4d31e58d2fb8b2fa9a5fe7474ac561b to your computer and use it in GitHub Desktop.
Base64 encoder golfing in JS
This file contains 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
(i,f=([a,b,c,...z])=>1+a?[a>>2,(a<<4)+(b>>4),b+1&&(b<<2)+(c>>6),c,...f(z)]:z)=>String.fromCharCode(...f(i).map(x=>(x%=64)+1?x+71-(x<26?6:x<52?0:x<62?75:x&1?87:90):61)) |
This file contains 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
(i, // input bytes iterable | |
f= // abuse the default value syntax to name our recursion fn | |
([a,b,c,...z])=> // eat three elements at a time and save any remaining to a new list `z` | |
1+a? // check if there were any bytes left to consume, 1+a prevents a=0 from being interpreted falsey as 0+1==true but undefined+1==false | |
[a>>2, // Take the upper six bits of the first byte by shifting two bits into the void: xxxxxx|xx | |
(a<<4)+(b>>4), // take the lower half of the first byte and upper half of the second | |
b+1&& // bitwise operators convert NaN to 0 and we want NaNs | |
(b<<2)+(c>>6), // lower 2 bits of the second byte with upper 4 of the third | |
c, // and take all of the last byte, we only want six bits for each of these so we'll lop of the upper two bits later | |
...f(z)]:z // continue with rest of z, or if a was undefined above, z will be an empty array so we return that to appease the spread operator | |
)=> | |
String.fromCharCode( // convert numbers to char string | |
...f(i) // call f with the input and curry the elements into fromCharCode to get a string without having to join them | |
.map(x=> // foreach number returned by f(i) | |
(x%=64)+1? // %64 leaves us with only the lower six bits and +1? checks for NaNs | |
x+71- // start at an offset into the ascii table | |
(x<26?6: // A-Z starts at 65 in ascii, so -6 | |
x<52?0: // a-z starts at 97 and x>26, x+71=97, so -0 | |
x<62?75: // 0-9 starts at 48, x>52, x+71-75=48, so -75 | |
x&1? // if we got this far it means the number is either 62 or 63, 62&1=0 and 63&1=1 | |
87: // 63&1=1, 63+71-87=47 => "/" | |
90) // 62&1=0, 62+71-90=43 => "+" | |
:61 // the NaN check above directed us here so output padding char, 61 => "=" | |
)) |
Had to beef up the NaN check as it was making 0x00 bytes in the input into padding chars lol
At 169 now!
167 with fixed checks on the recursion and remembering NaN+1===NaN and NaN is falsey
166 again!
f^63
to check for 62 vs 63 can be shortened to f&1
as we already know the value must be one of those two values
162! Used spread operator to pass chars in one go to String.fromCharCode
which saved a call to .join
167 :<
Had to add a NaN guard to prevent erroneous trailing nulls
> Buffer.from([0x8c, 0x82, 0x7f, 0x20]).toString('base64')
'jIJ/IA=='
> ((i,f=([a,b,c,...z])=>1+a?[a>>2,(a<<4)+(b>>4),b+1&&(b<<2)+(c>>6),c,...f(z)]:z)=>String.fromCharCode(...f(i).map(x=>(x%=64)+1?x+71-(x<26?6:x<52?0:x<62?75:x&1?87:90):61)))(Buffer.from([0x8c, 0x82, 0x7f, 0x20]))
'jIJ/IA=='
> ((i,f=([a,b,c,...z])=>1+a?[a>>2,(a<<4)+(b>>4),(b<<2)+(c>>6),c,...f(z)]:z)=>String.fromCharCode(...f(i).map(x=>(x%=64)+1?x+71-(x<26?6:x<52?0:x<62?75:x&1?87:90):61)))(Buffer.from([0x8c, 0x82, 0x7f, 0x20]))
'jIJ/IAA='
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Using array destructuring with recursion I got down to 172 with a static alphabet:
But then I found the very nice shortcut for the alphabet lookup given here:
https://codegolf.stackexchange.com/a/125830
So now we're at 166!