Skip to content

Instantly share code, notes, and snippets.

@liath
Last active May 21, 2024 19:37
Show Gist options
  • Save liath/f4d31e58d2fb8b2fa9a5fe7474ac561b to your computer and use it in GitHub Desktop.
Save liath/f4d31e58d2fb8b2fa9a5fe7474ac561b to your computer and use it in GitHub Desktop.
Base64 encoder golfing in JS
(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))
(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 => "="
))
@liath
Copy link
Author

liath commented May 18, 2024

162! Used spread operator to pass chars in one go to String.fromCharCode which saved a call to .join

@liath
Copy link
Author

liath commented May 21, 2024

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