~repos /plum

#treesitter#compiler#wasm

git clone https://pyrossh.dev/repos/plum.git

A statically typed, imperative programming language inspired by rust, python



libs/std/encoding/Base64.plum



`The Base64 package contains support for doing Base64 binary-to-text encodings.
module Base64
`Encode for PEM (RFC 1421).
encodePEM(data: ReadSeq[U8]) -> String =
encode(data, '+', '/', '=', 64)
`Encode for MIME (RFC 2045).
encodeMIME(data: ReadSeq[U8]) -> String =
encode(data, '+', '/', '=', 76)
`Encode for URLs (RFC 4648). Padding characters are stripped by default.
encodeURL(data: ReadSeq[U8], pad: Bool = False) -> String =
let c: U8 = if pad then '=' else 0 end
encode[A](data, '-', '_', c)
`Configurable encoding. The defaults are for RFC 4648.
encode(
data: ReadSeq[U8],
at62: U8 = '+',
at63: U8 = '/',
pad: U8 = '=',
linelen: USize = 0,
linesep: String = "\r\n"
) -> String =
len = ((data.size() + 2) / 3) * 4
out = A(len)
lineblocks = linelen / 4
srclen = data.size()
blocks = USize(0)
i = USize(0)
while srclen >= 3 do
let in1 = data(i)?
let in2 = data(i + 1)?
let in3 = data(i + 2)?
let out1 = in1 >> 2
let out2 = ((in1 and 0x03) << 4) + (in2 >> 4)
let out3 = ((in2 and 0x0f) << 2) + (in3 >> 6)
let out4 = in3 and 0x3f
out.push(encodeByte(out1, at62, at63)?)
out.push(encodeByte(out2, at62, at63)?)
out.push(encodeByte(out3, at62, at63)?)
out.push(encodeByte(out4, at62, at63)?)
i = i + 3
blocks = blocks + 1
srclen = srclen - 3
if (lineblocks > 0) and (blocks == lineblocks) then
out.append(linesep)
blocks = 0
if srclen >= 1 then
let in1 = data(i)?
let in2 = if srclen == 2 then data(i + 1)? else 0 end
let out1 = in1 >> 2
let out2 = ((in1 and 0x03) << 4) + (in2 >> 4)
let out3 = (in2 and 0x0f) << 2
out.push(encodeByte(out1, at62, at63)?)
out.push(encodeByte(out2, at62, at63)?)
if srclen == 2 then
out.push(encodeByte(out3, at62, at63)?)
else
out.push(pad)
out.push(pad)
if lineblocks > 0 then
out.append(linesep)
else
out.clear()
out
`Decode for URLs (RFC 4648).
decode_url(data: ReadSeq[U8]) -> Option[A] =
decode[A](data, '-', '_')?
`Configurable decoding. The defaults are for RFC 4648. Missing padding is
`not an error. Non-base64 data, other than whitespace (which can appear at
`any time), is an error.
decode(data: ReadSeq[U8], at62: U8 = '+', at63: U8 = '/', pad: U8 = '=') -> Option[A] =
len = (data.size() * 4) / 3
out = recover A(len) end
state = U8(0)
input = U8(0)
output = U8(0)
for i in Range(0, data.size())
input = data(i)?
let value =
match input
| ' ' | '\t' | '\r' | '\n' => continue
| pad => break
| at62 => 62
| at63 => 63
| if (input >= 'A') and (input <= 'Z') =>
(input - 'A')
| if (input >= 'a') and (input <= 'z') =>
((input - 'a') + 26)
| if (input >= '0') and (input <= '9') =>
((input - '0') + 52)
else
error
end
match state
| 0 =>
output = value << 2
state = 1
| 1 =>
out.push(output or (value >> 4))
output = (value and 0x0f) << 4
state = 2
| 2 =>
out.push(output or (value >> 2))
output = (value and 0x03) << 6
state = 3
| 3 =>
out.push(output or value)
state = 0
else
error
end
end
if output != 0 then
Fact(input != pad)?
match state
| 1 | 2 => out.push(output)
end
end
out
test "encode and decode" =>
expect:
encode(a) == b
decode(b) == a
where:
a | b
"" | ""
"f" | "Zg=="
"fo" | "Zm8="
"foo" | "Zm9v"
"foob" | "Zm9vYg=="
"fooba" | "Zm9vYmE="
"foobar" | "Zm9vYmFy"
encodeByte(i: Byte, at62: Byte, at63: Byte) -> Byte =
doc: "encode a single byte"
match i
i == 62 => at62
i == 63 => at63
i < 26 => 'A' + i
i < 52 => ('a' - 26) + i
i < 62 => ('0' - 52) + i
check:
expect:
encodeByte(i, at62, at63) == b
where:
i | at62 | at63
1 | 0 | 0
2 | 0 | 0
3 | 0 | 0
62 | 0 | 0
63 | 0 | 0