τau / encoding/base64 /

base64.tau

source
/Users/niconex/Documents/tau/stdlib/encoding/base64/base64.tau
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}