changeset 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 8096ba7082db
children 5825d48b27d1
files dmd/declaration.c dmd/declaration.h dmd/doc.c dmd/expression.c dmd/func.c dmd/mars.c dmd/module.c dmd/module.h dmd/mtype.c dmd/opover.c dmd/template.c dmd/template.h lphobos/phobos.d lphobos/std/base64.d
diffstat 14 files changed, 387 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/dmd/declaration.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/declaration.c	Fri Dec 28 23:52:40 2007 +0100
@@ -29,6 +29,7 @@
     : Dsymbol(id)
 {
     type = NULL;
+    originalType = NULL;
     storage_class = STCundefined;
     protection = PROTundefined;
     linkage = LINKdefault;
@@ -622,9 +623,13 @@
 	 * declarations.
 	 */
 	storage_class &= ~STCauto;
+	originalType = type;
     }
     else
+    {	if (!originalType)
+	    originalType = type;
 	type = type->semantic(loc, sc);
+    }
 
     type->checkDeprecated(loc, sc);
     linkage = sc->linkage;
--- a/dmd/declaration.h	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/declaration.h	Fri Dec 28 23:52:40 2007 +0100
@@ -91,6 +91,7 @@
 struct Declaration : Dsymbol
 {
     Type *type;
+    Type *originalType;		// before semantic analysis
     unsigned storage_class;
     enum PROT protection;
     enum LINK linkage;
@@ -114,6 +115,7 @@
     int isFinal()        { return storage_class & STCfinal; }
     int isAbstract()     { return storage_class & STCabstract; }
     int isConst()        { return storage_class & STCconst; }
+    int isInvariant()    { return 0; }
     int isAuto()         { return storage_class & STCauto; }
     int isScope()        { return storage_class & (STCscope | STCauto); }
     int isSynchronized() { return storage_class & STCsynchronized; }
--- a/dmd/doc.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/doc.c	Fri Dec 28 23:52:40 2007 +0100
@@ -269,7 +269,6 @@
     }
 
     buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars());
-
     if (isDocFile)
     {
 	size_t commentlen = strlen((char *)comment);
@@ -283,7 +282,6 @@
     }
     else
     {
-
 	dc->writeSections(sc, this, sc->docbuf);
 	emitMemberComments(sc);
     }
@@ -654,6 +652,10 @@
 	    buf->writestring("static ");
 	if (d->isConst())
 	    buf->writestring("const ");
+#if V2
+	if (d->isInvariant())
+	    buf->writestring("invariant ");
+#endif
 	if (d->isFinal())
 	    buf->writestring("final ");
 	if (d->isSynchronized())
@@ -663,7 +665,7 @@
 
 void Declaration::toDocBuffer(OutBuffer *buf)
 {
-    //printf("Declaration::toDocbuffer() %s\n", toChars());
+    //printf("Declaration::toDocbuffer() %s, originalType = %p\n", toChars(), originalType);
     if (ident)
     {
 	prefix(buf, this);
@@ -671,7 +673,12 @@
 	if (type)
 	{   HdrGenState hgs;
 	    hgs.ddoc = 1;
-	    type->toCBuffer(buf, ident, &hgs);
+	    if (originalType)
+	    {	//originalType->print();
+		originalType->toCBuffer(buf, ident, &hgs);
+	    }
+	    else
+		type->toCBuffer(buf, ident, &hgs);
 	}
 	else
 	    buf->writestring(ident->toChars());
@@ -723,10 +730,11 @@
 	    td->onemember == this)
 	{   HdrGenState hgs;
 	    unsigned o = buf->offset;
+	    TypeFunction *tf = (TypeFunction *)type;
 
 	    hgs.ddoc = 1;
 	    prefix(buf, td);
-	    type->next->toCBuffer(buf, NULL, &hgs);
+	    tf->next->toCBuffer(buf, NULL, &hgs);
 	    buf->writeByte(' ');
 	    buf->writestring(ident->toChars());
 	    buf->writeByte('(');
@@ -738,7 +746,6 @@
 		tp->toCBuffer(buf, &hgs);
 	    }
 	    buf->writeByte(')');
-	    TypeFunction *tf = (TypeFunction *)type;
 	    Argument::argsToCBuffer(buf, &hgs, tf->parameters, tf->varargs);
 	    buf->writestring(";\n");
 
--- a/dmd/expression.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/expression.c	Fri Dec 28 23:52:40 2007 +0100
@@ -1270,7 +1270,7 @@
 
 char *RealExp::toChars()
 {
-    static char buffer[sizeof(value) * 3 + 8 + 1 + 1];
+    char buffer[sizeof(value) * 3 + 8 + 1 + 1];
 
 #ifdef IN_GCC
     value.format(buffer, sizeof(buffer));
@@ -1280,7 +1280,7 @@
     sprintf(buffer, type->isimaginary() ? "%Lgi" : "%Lg", value);
 #endif
     assert(strlen(buffer) < sizeof(buffer));
-    return buffer;
+    return mem.strdup(buffer);
 }
 
 integer_t RealExp::toInteger()
@@ -1484,7 +1484,7 @@
 
 char *ComplexExp::toChars()
 {
-    static char buffer[sizeof(value) * 3 + 8 + 1];
+    char buffer[sizeof(value) * 3 + 8 + 1];
 
 #ifdef IN_GCC
     char buf1[sizeof(value) * 3 + 8 + 1];
@@ -1496,7 +1496,7 @@
     sprintf(buffer, "(%Lg+%Lgi)", creall(value), cimagl(value));
     assert(strlen(buffer) < sizeof(buffer));
 #endif
-    return buffer;
+    return mem.strdup(buffer);
 }
 
 integer_t ComplexExp::toInteger()
@@ -3140,7 +3140,13 @@
 	if (cd->isInterfaceDeclaration())
 	    error("cannot create instance of interface %s", cd->toChars());
 	else if (cd->isAbstract())
-	    error("cannot create instance of abstract class %s", cd->toChars());
+	{   error("cannot create instance of abstract class %s", cd->toChars());
+	    for (int i = 0; i < cd->vtbl.dim; i++)
+	    {	FuncDeclaration *fd = ((Dsymbol *)cd->vtbl.data[i])->isFuncDeclaration();
+		if (fd && fd->isAbstract())
+		    error("function %s is abstract", fd->toChars());
+	    }
+	}
 	checkDeprecated(sc, cd);
 	if (cd->isNested())
 	{   /* We need a 'this' pointer for the nested class.
@@ -5528,9 +5534,9 @@
 	    TemplateDeclaration *td = dte->td;
 	    assert(td);
 	    if (!arguments)
-		// Should fix deduce() so it works on NULL argument
+		// Should fix deduceFunctionTemplate() so it works on NULL argument
 		arguments = new Expressions();
-	    f = td->deduce(sc, loc, NULL, arguments);
+	    f = td->deduceFunctionTemplate(sc, loc, NULL, arguments);
 	    if (!f)
 	    {	type = Type::terror;
 		return this;
@@ -5705,7 +5711,7 @@
 	else if (e1->op == TOKtemplate)
 	{
 	    TemplateExp *te = (TemplateExp *)e1;
-	    f = te->td->deduce(sc, loc, NULL, arguments);
+	    f = te->td->deduceFunctionTemplate(sc, loc, NULL, arguments);
 	    if (!f)
 	    {	type = Type::terror;
 		return this;
--- a/dmd/func.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/func.c	Fri Dec 28 23:52:40 2007 +0100
@@ -145,6 +145,9 @@
 
     if (isAbstract() && !isVirtual())
 	error("non-virtual functions cannot be abstract");
+
+    if (isAbstract() && isFinal())
+	error("cannot be both final and abstract");
 #if 0
     if (isAbstract() && fbody)
 	error("abstract functions cannot have bodies");
--- a/dmd/mars.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/mars.c	Fri Dec 28 23:52:40 2007 +0100
@@ -71,7 +71,7 @@
     copyright = "Copyright (c) 1999-2007 by Digital Mars and Tomas Lindquist Olsen";
     written = "written by Walter Bright and Tomas Lindquist Olsen";
     llvmdc_version = "0.1";
-    version = "v1.023";
+    version = "v1.024";
     global.structalign = 8;
 
     memset(&params, 0, sizeof(Param));
--- a/dmd/module.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/module.c	Fri Dec 28 23:52:40 2007 +0100
@@ -87,6 +87,7 @@
     vmoduleinfo = NULL;
     massert = NULL;
     marray = NULL;
+    sictor = NULL;
     sctor = NULL;
     sdtor = NULL;
     stest = NULL;
--- a/dmd/module.h	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/module.h	Fri Dec 28 23:52:40 2007 +0100
@@ -147,6 +147,7 @@
     Symbol *cov;		// private uint[] __coverage;
     unsigned *covb;		// bit array of valid code line numbers
 
+    Symbol *sictor;		// module order independent constructor
     Symbol *sctor;		// module constructor
     Symbol *sdtor;		// module destructor
     Symbol *stest;		// module unit test
--- a/dmd/mtype.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/mtype.c	Fri Dec 28 23:52:40 2007 +0100
@@ -2742,38 +2742,50 @@
     }
     //printf("TypeFunction::semantic() this = %p\n", this);
 
-    linkage = sc->linkage;
-    if (!next)
+    TypeFunction *tf = (TypeFunction *)mem.malloc(sizeof(TypeFunction));
+    memcpy(tf, this, sizeof(TypeFunction));
+    if (parameters)
+    {	tf->parameters = (Arguments *)parameters->copy();
+	for (size_t i = 0; i < parameters->dim; i++)
+	{   Argument *arg = (Argument *)parameters->data[i];
+	    Argument *cpy = (Argument *)mem.malloc(sizeof(Argument));
+	    memcpy(cpy, arg, sizeof(Argument));
+	    tf->parameters->data[i] = (void *)cpy;
+	}
+    }
+
+    tf->linkage = sc->linkage;
+    if (!tf->next)
     {
 	assert(global.errors);
-	next = tvoid;
-    }
-    next = next->semantic(loc,sc);
-    if (next->toBasetype()->ty == Tsarray)
-    {	error(loc, "functions cannot return static array %s", next->toChars());
-	next = Type::terror;
-    }
-    if (next->toBasetype()->ty == Tfunction)
+	tf->next = tvoid;
+    }
+    tf->next = tf->next->semantic(loc,sc);
+    if (tf->next->toBasetype()->ty == Tsarray)
+    {	error(loc, "functions cannot return static array %s", tf->next->toChars());
+	tf->next = Type::terror;
+    }
+    if (tf->next->toBasetype()->ty == Tfunction)
     {	error(loc, "functions cannot return a function");
-	next = Type::terror;
-    }
-    if (next->toBasetype()->ty == Ttuple)
+	tf->next = Type::terror;
+    }
+    if (tf->next->toBasetype()->ty == Ttuple)
     {	error(loc, "functions cannot return a tuple");
-	next = Type::terror;
-    }
-    if (next->isauto() && !(sc->flags & SCOPEctor))
-	error(loc, "functions cannot return auto %s", next->toChars());
-
-    if (parameters)
-    {	size_t dim = Argument::dim(parameters);
+	tf->next = Type::terror;
+    }
+    if (tf->next->isauto() && !(sc->flags & SCOPEctor))
+	error(loc, "functions cannot return auto %s", tf->next->toChars());
+
+    if (tf->parameters)
+    {	size_t dim = Argument::dim(tf->parameters);
 
 	for (size_t i = 0; i < dim; i++)
-	{   Argument *arg = Argument::getNth(parameters, i);
+	{   Argument *arg = Argument::getNth(tf->parameters, i);
 	    Type *t;
 
-	    inuse++;
+	    tf->inuse++;
 	    arg->type = arg->type->semantic(loc,sc);
-	    if (inuse == 1) inuse--;
+	    if (tf->inuse == 1) tf->inuse--;
 	    t = arg->type->toBasetype();
 
 	    if (arg->storageClass & (STCout | STCref | STClazy))
@@ -2795,27 +2807,27 @@
 	     * change.
 	     */
 	    if (t->ty == Ttuple)
-	    {	dim = Argument::dim(parameters);
+	    {	dim = Argument::dim(tf->parameters);
 		i--;
 	    }
 	}
     }
-    deco = merge()->deco;
-
-    if (inuse)
+    tf->deco = tf->merge()->deco;
+
+    if (tf->inuse)
     {	error(loc, "recursive type");
-	inuse = 0;
+	tf->inuse = 0;
 	return terror;
     }
 
-    if (varargs == 1 && linkage != LINKd && Argument::dim(parameters) == 0)
+    if (tf->varargs == 1 && tf->linkage != LINKd && Argument::dim(tf->parameters) == 0)
 	error(loc, "variadic functions with non-D linkage must have at least one parameter");
 
     /* Don't return merge(), because arg identifiers and default args
      * can be different
      * even though the types match
      */
-    return this;
+    return tf;
 }
 
 /********************************
--- a/dmd/opover.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/opover.c	Fri Dec 28 23:52:40 2007 +0100
@@ -729,7 +729,7 @@
     FuncDeclaration *fd;
 
     assert(td);
-    fd = td->deduce(sc, loc, targsi, arguments);
+    fd = td->deduceFunctionTemplate(sc, loc, targsi, arguments);
     if (!fd)
 	return;
     m->anyf = fd;
--- a/dmd/template.c	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/template.c	Fri Dec 28 23:52:40 2007 +0100
@@ -647,7 +647,7 @@
  *	dedargs		Expression/Type deduced template arguments
  */
 
-MATCH TemplateDeclaration::deduceMatch(Objects *targsi, Expressions *fargs,
+MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Objects *targsi, Expressions *fargs,
 	Objects *dedargs)
 {
     size_t i;
@@ -662,7 +662,7 @@
     Objects dedtypes;	// for T:T*, the dedargs is the T*, dedtypes is the T
 
 #if 0
-    printf("\nTemplateDeclaration::deduceMatch() %s\n", toChars());
+    printf("\nTemplateDeclaration::deduceFunctionTemplateMatch() %s\n", toChars());
     for (i = 0; i < fargs->dim; i++)
     {	Expression *e = (Expression *)fargs->data[i];
 	printf("\tfarg[%d] is %s, type is %s\n", i, e->toChars(), e->type->toChars());
@@ -985,7 +985,7 @@
  *	fargs		arguments to function
  */
 
-FuncDeclaration *TemplateDeclaration::deduce(Scope *sc, Loc loc,
+FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc,
 	Objects *targsi, Expressions *fargs)
 {
     MATCH m_best = MATCHnomatch;
@@ -996,7 +996,7 @@
     FuncDeclaration *fd;
 
 #if 0
-    printf("TemplateDeclaration::deduce() %s\n", toChars());
+    printf("TemplateDeclaration::deduceFunctionTemplate() %s\n", toChars());
     printf("    targsi:\n");
     if (targsi)
     {	for (int i = 0; i < targsi->dim; i++)
@@ -1028,8 +1028,8 @@
 	MATCH m;
 	Objects dedargs;
 
-	m = td->deduceMatch(targsi, fargs, &dedargs);
-	//printf("deduceMatch = %d\n", m);
+	m = td->deduceFunctionTemplateMatch(targsi, fargs, &dedargs);
+	//printf("deduceFunctionTemplateMatch = %d\n", m);
 	if (!m)			// if no match
 	    continue;
 
--- a/dmd/template.h	Fri Dec 28 22:55:24 2007 +0100
+++ b/dmd/template.h	Fri Dec 28 23:52:40 2007 +0100
@@ -71,8 +71,8 @@
     MATCH matchWithInstance(TemplateInstance *ti, Objects *atypes, int flag);
     int leastAsSpecialized(TemplateDeclaration *td2);
 
-    MATCH deduceMatch(Objects *targsi, Expressions *fargs, Objects *dedargs);
-    FuncDeclaration *deduce(Scope *sc, Loc loc, Objects *targsi, Expressions *fargs);
+    MATCH deduceFunctionTemplateMatch(Objects *targsi, Expressions *fargs, Objects *dedargs);
+    FuncDeclaration *deduceFunctionTemplate(Scope *sc, Loc loc, Objects *targsi, Expressions *fargs);
     void declareParameter(Scope *sc, TemplateParameter *tp, Object *o);
 
     TemplateDeclaration *isTemplateDeclaration() { return this; }
--- a/lphobos/phobos.d	Fri Dec 28 22:55:24 2007 +0100
+++ b/lphobos/phobos.d	Fri Dec 28 23:52:40 2007 +0100
@@ -2,16 +2,18 @@
 
 import
 std.array,
+std.base64,
 std.ctype,
 std.format,
 std.intrinsic,
 std.math,
 std.moduleinit,
 std.outofmemory,
+std.stdarg,
 std.stdint,
 std.stdio,
-std.stdarg,
 std.string,
+std.traits,
 std.uni,
 std.utf,
 
--- /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!");
+}
+