tomo/examples/base64/base64.tm
2024-11-04 01:33:54 -05:00

97 lines
4.0 KiB
Tcl

# Base 64 encoding and decoding
_enc := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/":utf8_bytes()
_EQUAL_BYTE := 0x3D[B]
_dec := [
255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 255[B], 255[B], 62[B], 255[B], 255[B], 255[B], 63[B],
52[B], 53[B], 54[B], 55[B], 56[B], 57[B], 58[B], 59[B],
60[B], 61[B], 255[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 0[B], 1[B], 2[B], 3[B], 4[B], 5[B], 6[B],
7[B], 8[B], 9[B], 10[B], 11[B], 12[B], 13[B], 14[B],
15[B], 16[B], 17[B], 18[B], 19[B], 20[B], 21[B], 22[B],
23[B], 24[B], 25[B], 255[B], 255[B], 255[B], 255[B], 255[B],
255[B], 26[B], 27[B], 28[B], 29[B], 30[B], 31[B], 32[B],
33[B], 34[B], 35[B], 36[B], 37[B], 38[B], 39[B], 40[B],
41[B], 42[B], 43[B], 44[B], 45[B], 46[B], 47[B], 48[B],
49[B], 50[B], 51[B], 255[B], 255[B], 255[B], 255[B], 255[B],
]
lang Base64:
func from_text(text:Text -> Base64?):
return Base64.from_bytes(text:utf8_bytes())
func from_bytes(bytes:[Byte] -> Base64?):
output := [0[B] for _ in bytes.length * 4 / 3 + 4]
src := 1[64]
dest := 1[64]
while src + 2[64] <= bytes.length:
chunk24 := (
(Int32(bytes[src]) <<< 16) or (Int32(bytes[src+1[64]]) <<< 8) or Int32(bytes[src+2[64]])
)
src += 3
output[dest] = _enc[1[32] + ((chunk24 >>> 18) and 0b111111[32])]
output[dest+1[64]] = _enc[1[32] + ((chunk24 >>> 12) and 0b111111[32])]
output[dest+2[64]] = _enc[1[32] + ((chunk24 >>> 6) and 0b111111[32])]
output[dest+3[64]] = _enc[1[32] + (chunk24 and 0b111111[32])]
dest += 4
if src + 1[64] == bytes.length:
chunk16 := (
(Int32(bytes[src]) <<< 8) or Int32(bytes[src+1[64]])
)
output[dest] = _enc[1[32] + ((chunk16 >>> 10) and 0b111111[32])]
output[dest+1[64]] = _enc[1[32] + ((chunk16 >>> 4) and 0b111111[32])]
output[dest+2[64]] = _enc[1[32] + ((chunk16 <<< 2)and 0b111111[32])]
output[dest+3[64]] = _EQUAL_BYTE
else if src == bytes.length:
chunk8 := Int32(bytes[src])
output[dest] = _enc[1[32] + ((chunk8 >>> 2) and 0b111111[32])]
output[dest+1[64]] = _enc[1[32] + ((chunk8 <<< 4) and 0b111111[32])]
output[dest+2[64]] = _EQUAL_BYTE
output[dest+3[64]] = _EQUAL_BYTE
return Base64.without_escaping(Text.from_bytes(output) or return !Base64)
func decode_text(b64:Base64 -> Text?):
return Text.from_bytes(b64:decode_bytes() or return !Text)
func decode_bytes(b64:Base64 -> [Byte]?):
bytes := b64.text_content:utf8_bytes()
output := [0[B] for _ in bytes.length/4 * 3]
src := 1[64]
dest := 1[64]
while src + 3[64] <= bytes.length:
chunk24 := (
(Int32(_dec[1[B]+bytes[src]]) <<< 18) or
(Int32(_dec[1[B]+bytes[src+1[64]]]) <<< 12) or
(Int32(_dec[1[B]+bytes[src+2[64]]]) <<< 6) or
Int32(_dec[1[B]+bytes[src+3[64]]])
)
src += 4
output[dest] = Byte((chunk24 >>> 16) and 0xFF[32])
output[dest+1[64]] = Byte((chunk24 >>> 8) and 0xFF[32])
output[dest+2[64]] = Byte(chunk24 and 0xFF[32])
dest += 3
while output[-1] == 0xFF[B]:
output = output:to(-2)
return output
func main(input=(/dev/stdin), decode=no):
if decode:
b := Base64.without_escaping(input:read()!)
say(b:decode_text()!)
else:
text := input:read()!
say(Base64.from_text(text)!.text_content)