~repos /plum
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