1# base64 - the encoding of RFC 4648.2#3# EncodeToString uses the standard alphabet and pads with "=", which is what4# most things expect. URLEncodeToString uses "-" and "_" instead of "+" and5# "/", for what travels inside a URL or a filename.67stdAlphabet = bytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")8urlAlphabet = bytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")910pad = 611112# EncodedLen is how long the encoding of n bytes is, padding included.13EncodedLen = fn(n) { ((n + 2) - (n + 2) % 3) / 3 * 4 }1415# encode is the encoder both alphabets go through.16encode = fn(data, alphabet, padded) {17 if type(data) != "bytes" {18 if failed(data = bytes(string(data))) {19 return data20 }21 }2223 out = []24 i = 02526 # Three bytes at a time become four digits of six bits each.27 for i + 3 <= len(data) {28 n = data[i] << 16 | data[i + 1] << 8 | data[i + 2]29 out = append(out,30 alphabet[n >> 18 & 0x3f],31 alphabet[n >> 12 & 0x3f],32 alphabet[n >> 6 & 0x3f],33 alphabet[n & 0x3f])34 i = i + 335 }3637 # What is left is one or two bytes, the missing bits counting as zero.38 rest = len(data) - i39 if rest == 1 {40 n = data[i] << 1641 out = append(out, alphabet[n >> 18 & 0x3f], alphabet[n >> 12 & 0x3f])42 if padded {43 out = append(out, pad, pad)44 }45 } else if rest == 2 {46 n = data[i] << 16 | data[i + 1] << 847 out = append(out, alphabet[n >> 18 & 0x3f], alphabet[n >> 12 & 0x3f], alphabet[n >> 6 & 0x3f])48 if padded {49 out = append(out, pad)50 }51 }5253 return string(bytes(out))54}5556# values maps a digit to the six bits it stands for. Both alphabets share the57# first 62 digits, so one table serves them both.58value = fn(c) {59 if c >= 65 && c <= 90 {60 return c - 6561 }62 if c >= 97 && c <= 122 {63 return c - 7164 }65 if c >= 48 && c <= 57 {66 return c + 467 }68 if c == 43 || c == 45 {69 return 6270 }71 if c == 47 || c == 95 {72 return 6373 }74 return -175}7677# EncodeToString returns the base64 form of data, a string or bytes.78EncodeToString = fn(data) { encode(data, stdAlphabet, true) }7980# URLEncodeToString is EncodeToString with the URL safe alphabet and no81# padding, the form a token or a filename is written in.82URLEncodeToString = fn(data) { encode(data, urlAlphabet, false) }8384# DecodeString returns the bytes s stands for. Both alphabets are understood85# and the padding is optional, since it carries nothing the length doesn't.86DecodeString = fn(s) {87 if type(s) != "bytes" {88 if failed(s = bytes(string(s))) {89 return s90 }91 }9293 out = []94 n = 095 bits = 09697 for i = 0; i < len(s); ++i {98 c = s[i]99100 # Padding and the newlines a long encoding is broken with carry101 # nothing.102 if c == pad || c == 10 || c == 13 {103 continue104 }105106 v = value(c)107 if v < 0 {108 return error("base64: invalid character at index {i}")109 }110111 n = n << 6 | v112 bits = bits + 6113114 # Every time six more bits make a whole byte, out gets one.115 if bits >= 8 {116 bits = bits - 8117 out = append(out, n >> bits & 0xff)118 }119 }120121 return bytes(out)122}