Mercurial > projects > ldc
diff lphobos/std/base64.d @ 130:a7dfa0ed966c trunk
[svn r134] Merged the DMD 1.024 frontend.
Added std.base64.
author | lindquist |
---|---|
date | Fri, 28 Dec 2007 23:52:40 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/std/base64.d Fri Dec 28 23:52:40 2007 +0100 @@ -0,0 +1,293 @@ +/** + * Encodes/decodes MIME base64 data. + * + * Macros: + * WIKI=Phobos/StdBase64 + * References: + * <a href="http://en.wikipedia.org/wiki/Base64">Wikipedia Base64</a>$(BR) + * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>$(BR) + */ + + +/* base64.d + * Modified from C. Miller's version, his copyright is below. + */ + +/* + Copyright (C) 2004 Christopher E. Miller + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +module std.base64; + +/** + */ + +class Base64Exception: Exception +{ + this(char[] msg) + { + super(msg); + } +} + + +/** + */ + +class Base64CharException: Base64Exception +{ + this(char[] msg) + { + super(msg); + } +} + + +const char[] array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +/** + * Returns the number of bytes needed to encode a string of length slen. + */ + +uint encodeLength(uint slen) +{ + uint result; + result = slen / 3; + if(slen % 3) + result++; + return result * 4; +} + +/** + * Encodes str[] and places the result in buf[]. + * Params: + * str = string to encode + * buf = destination buffer, must be large enough for the result. + * Returns: + * slice into buf[] representing encoded result + */ + +char[] encode(char[] str, char[] buf) +in +{ + assert(buf.length >= encodeLength(str.length)); +} +body +{ + if(!str.length) + return buf[0 .. 0]; + + uint stri; + uint strmax = str.length / 3; + uint strleft = str.length % 3; + uint x; + char* sp, bp; + + bp = &buf[0]; + sp = &str[0]; + for(stri = 0; stri != strmax; stri++) + { + x = (sp[0] << 16) | (sp[1] << 8) | (sp[2]); + sp+= 3; + *bp++ = array[(x & 0b11111100_00000000_00000000) >> 18]; + *bp++ = array[(x & 0b00000011_11110000_00000000) >> 12]; + *bp++ = array[(x & 0b00000000_00001111_11000000) >> 6]; + *bp++ = array[(x & 0b00000000_00000000_00111111)]; + } + + switch(strleft) + { + case 2: + x = (sp[0] << 16) | (sp[1] << 8); + sp += 2; + *bp++ = array[(x & 0b11111100_00000000_00000000) >> 18]; + *bp++ = array[(x & 0b00000011_11110000_00000000) >> 12]; + *bp++ = array[(x & 0b00000000_00001111_11000000) >> 6]; + *bp++ = '='; + break; + + case 1: + x = *sp++ << 16; + *bp++ = array[(x & 0b11111100_00000000_00000000) >> 18]; + *bp++ = array[(x & 0b00000011_11110000_00000000) >> 12]; + *bp++ = '='; + *bp++ = '='; + break; + + case 0: + break; + + default: + assert(0); + } + + return buf[0 .. (bp - &buf[0])]; +} + + +/** + * Encodes str[] and returns the result. + */ + +char[] encode(char[] str) +{ + return encode(str, new char[encodeLength(str.length)]); +} + + +unittest +{ + assert(encode("f") == "Zg=="); + assert(encode("fo") == "Zm8="); + assert(encode("foo") == "Zm9v"); + assert(encode("foos") == "Zm9vcw=="); + assert(encode("all your base64 are belong to foo") == "YWxsIHlvdXIgYmFzZTY0IGFyZSBiZWxvbmcgdG8gZm9v"); +} + + +/** + * Returns the number of bytes needed to decode an encoded string of this + * length. + */ +uint decodeLength(uint elen) +{ + return elen / 4 * 3; +} + + +/** + * Decodes str[] and places the result in buf[]. + * Params: + * str = string to encode + * buf = destination buffer, must be large enough for the result. + * Returns: + * slice into buf[] representing encoded result + * Errors: + * Throws Base64Exception on invalid base64 encoding in estr[]. + * Throws Base64CharException on invalid base64 character in estr[]. + */ +char[] decode(char[] estr, char[] buf) +in +{ + assert(buf.length + 2 >= decodeLength(estr.length)); //account for '=' padding +} +body +{ + void badc(char ch) + { + throw new Base64CharException("Invalid base64 character '" ~ (&ch)[0 .. 1] ~ "'"); + } + + + uint arrayIndex(char ch) + out(result) + { + assert(ch == array[result]); + } + body + { + if(ch >= 'A' && ch <= 'Z') + return ch - 'A'; + if(ch >= 'a' && ch <= 'z') + return 'Z' - 'A' + 1 + ch - 'a'; + if(ch >= '0' && ch <= '9') + return 'Z' - 'A' + 1 + 'z' - 'a' + 1 + ch - '0'; + if(ch == '+') + return 'Z' - 'A' + 1 + 'z' - 'a' + 1 + '9' - '0' + 1; + if(ch == '/') + return 'Z' - 'A' + 1 + 'z' - 'a' + 1 + '9' - '0' + 1 + 1; + badc(ch); + assert(0); + } + + + if(!estr.length) + return buf[0 .. 0]; + + if(estr.length % 4) + throw new Base64Exception("Invalid encoded base64 string"); + + uint estri; + uint estrmax = estr.length / 4; + uint x; + char* sp, bp; + char ch; + + sp = &estr[0]; + bp = &buf[0]; + for(estri = 0; estri != estrmax; estri++) + { + x = arrayIndex(sp[0]) << 18 | arrayIndex(sp[1]) << 12; + sp += 2; + + ch = *sp++; + if(ch == '=') + { + if(*sp++ != '=') + badc('='); + *bp++ = cast(char) (x >> 16); + break; + } + x |= arrayIndex(ch) << 6; + + ch = *sp++; + if(ch == '=') + { + *bp++ = cast(char) (x >> 16); + *bp++ = cast(char) ((x >> 8) & 0xFF); + break; + } + x |= arrayIndex(ch); + + *bp++ = cast(char) (x >> 16); + *bp++ = cast(char) ((x >> 8) & 0xFF); + *bp++ = cast(char) (x & 0xFF); + } + + return buf[0 .. (bp - &buf[0])]; +} + +/** + * Decodes estr[] and returns the result. + * Errors: + * Throws Base64Exception on invalid base64 encoding in estr[]. + * Throws Base64CharException on invalid base64 character in estr[]. + */ + +char[] decode(char[] estr) +{ + return decode(estr, new char[decodeLength(estr.length)]); +} + + +unittest +{ + assert(decode(encode("f")) == "f"); + assert(decode(encode("fo")) == "fo"); + assert(decode(encode("foo")) == "foo"); + assert(decode(encode("foos")) == "foos"); + assert(decode(encode("all your base64 are belong to foo")) == "all your base64 are belong to foo"); + + assert(decode(encode("testing some more")) == "testing some more"); + assert(decode(encode("asdf jkl;")) == "asdf jkl;"); + assert(decode(encode("base64 stuff")) == "base64 stuff"); + assert(decode(encode("\1\2\3\4\5\6\7foo\7\6\5\4\3\2\1!")) == "\1\2\3\4\5\6\7foo\7\6\5\4\3\2\1!"); +} +