DEFINE shl == 2 *; shr == 2 /; and63 == [64 / 64 *] nullary -; b64enc1 == ord 16 [shl] times [ord 8 [shl] times] dip + [ord] dip +; b64enc2 == "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" swap [] [ [18 [shr] times and63 at] [12 [shr] times and63 at] [6 [shr] times and63 at] [and63 at] ] construct "" cons cons cons cons [pop] dip; s2l == [null] [pop []] [uncons] [cons] linrec; b64enc == s2l "" swap # pad with null bytes # calculate how many bytes to pad [size 3 rem 3 swap - 3 rem] nullary # save the amount for later dup [swap] dip [[swap] dip] dip # i cant use null bytes [0 chr [] cons concat] times # len(s)/3 times [size 3 /] nullary [[3 take] nullary s2l [b64enc1 b64enc2] infra first rollup [swap concat] dip 3 drop] times # remove the now-empty string pop # add the padding # encoded characters - characters that need to be padded [size] nullary swapd [swap -] unary # take only the non padded characters swapd take # add back as many ='s as you took # for some reason this part crahes joy with a malloc error if the input is more than 20 characters swap ["=" concat] times "\n" concat END stdin [feof not] [fgets b64enc putchars] while fclose.