diff --git a/dmd/declaration.c b/dmd/declaration.c
index f9eac6db..62fdf1b4 100644
--- a/dmd/declaration.c
+++ b/dmd/declaration.c
@@ -29,6 +29,7 @@ Declaration::Declaration(Identifier *id)
: Dsymbol(id)
{
type = NULL;
+ originalType = NULL;
storage_class = STCundefined;
protection = PROTundefined;
linkage = LINKdefault;
@@ -622,9 +623,13 @@ void VarDeclaration::semantic(Scope *sc)
* declarations.
*/
storage_class &= ~STCauto;
+ originalType = type;
}
else
+ { if (!originalType)
+ originalType = type;
type = type->semantic(loc, sc);
+ }
type->checkDeprecated(loc, sc);
linkage = sc->linkage;
diff --git a/dmd/declaration.h b/dmd/declaration.h
index 28535c43..c190ae51 100644
--- a/dmd/declaration.h
+++ b/dmd/declaration.h
@@ -91,6 +91,7 @@ int overloadApply(FuncDeclaration *fstart,
struct Declaration : Dsymbol
{
Type *type;
+ Type *originalType; // before semantic analysis
unsigned storage_class;
enum PROT protection;
enum LINK linkage;
@@ -114,6 +115,7 @@ struct Declaration : Dsymbol
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; }
diff --git a/dmd/doc.c b/dmd/doc.c
index 63bde67d..41ce5675 100644
--- a/dmd/doc.c
+++ b/dmd/doc.c
@@ -269,7 +269,6 @@ void Module::gendocfile()
}
buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars());
-
if (isDocFile)
{
size_t commentlen = strlen((char *)comment);
@@ -283,7 +282,6 @@ void Module::gendocfile()
}
else
{
-
dc->writeSections(sc, this, sc->docbuf);
emitMemberComments(sc);
}
@@ -654,6 +652,10 @@ void prefix(OutBuffer *buf, Dsymbol *s)
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 prefix(OutBuffer *buf, Dsymbol *s)
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 @@ void Declaration::toDocBuffer(OutBuffer *buf)
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 @@ void FuncDeclaration::toDocBuffer(OutBuffer *buf)
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 @@ void FuncDeclaration::toDocBuffer(OutBuffer *buf)
tp->toCBuffer(buf, &hgs);
}
buf->writeByte(')');
- TypeFunction *tf = (TypeFunction *)type;
Argument::argsToCBuffer(buf, &hgs, tf->parameters, tf->varargs);
buf->writestring(";\n");
diff --git a/dmd/expression.c b/dmd/expression.c
index 4c8ff19f..1434ced9 100644
--- a/dmd/expression.c
+++ b/dmd/expression.c
@@ -1270,7 +1270,7 @@ RealExp::RealExp(Loc loc, real_t value, Type *type)
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 @@ char *RealExp::toChars()
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 @@ ComplexExp::ComplexExp(Loc loc, complex_t value, Type *type)
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 @@ char *ComplexExp::toChars()
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 @@ Lagain:
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 @@ Lagain:
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 @@ Lagain:
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;
diff --git a/dmd/func.c b/dmd/func.c
index 9f52780f..d93088aa 100644
--- a/dmd/func.c
+++ b/dmd/func.c
@@ -145,6 +145,9 @@ void FuncDeclaration::semantic(Scope *sc)
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");
diff --git a/dmd/mars.c b/dmd/mars.c
index 354c32c7..83394a78 100644
--- a/dmd/mars.c
+++ b/dmd/mars.c
@@ -71,7 +71,7 @@ Global::Global()
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(¶ms, 0, sizeof(Param));
diff --git a/dmd/module.c b/dmd/module.c
index 44666450..d691aaa3 100644
--- a/dmd/module.c
+++ b/dmd/module.c
@@ -87,6 +87,7 @@ Module::Module(char *filename, Identifier *ident, int doDocComment, int doHdrGen
vmoduleinfo = NULL;
massert = NULL;
marray = NULL;
+ sictor = NULL;
sctor = NULL;
sdtor = NULL;
stest = NULL;
diff --git a/dmd/module.h b/dmd/module.h
index 8834b584..7b68da64 100644
--- a/dmd/module.h
+++ b/dmd/module.h
@@ -147,6 +147,7 @@ struct Module : Package
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
diff --git a/dmd/mtype.c b/dmd/mtype.c
index 8547a93e..893733b9 100644
--- a/dmd/mtype.c
+++ b/dmd/mtype.c
@@ -2742,38 +2742,50 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc)
}
//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;
+ tf->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;
+ 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 (next->toBasetype()->ty == Tfunction)
+ if (tf->next->toBasetype()->ty == Tfunction)
{ error(loc, "functions cannot return a function");
- next = Type::terror;
+ tf->next = Type::terror;
}
- if (next->toBasetype()->ty == Ttuple)
+ if (tf->next->toBasetype()->ty == Ttuple)
{ error(loc, "functions cannot return a tuple");
- next = Type::terror;
+ tf->next = Type::terror;
}
- if (next->isauto() && !(sc->flags & SCOPEctor))
- error(loc, "functions cannot return auto %s", next->toChars());
+ if (tf->next->isauto() && !(sc->flags & SCOPEctor))
+ error(loc, "functions cannot return auto %s", tf->next->toChars());
- if (parameters)
- { size_t dim = Argument::dim(parameters);
+ 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 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc)
* change.
*/
if (t->ty == Ttuple)
- { dim = Argument::dim(parameters);
+ { dim = Argument::dim(tf->parameters);
i--;
}
}
}
- deco = merge()->deco;
+ tf->deco = tf->merge()->deco;
- if (inuse)
+ 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;
}
/********************************
diff --git a/dmd/opover.c b/dmd/opover.c
index 5b4f028d..c6b7940e 100644
--- a/dmd/opover.c
+++ b/dmd/opover.c
@@ -729,7 +729,7 @@ static void templateResolve(Match *m, TemplateDeclaration *td, Scope *sc, Loc lo
FuncDeclaration *fd;
assert(td);
- fd = td->deduce(sc, loc, targsi, arguments);
+ fd = td->deduceFunctionTemplate(sc, loc, targsi, arguments);
if (!fd)
return;
m->anyf = fd;
diff --git a/dmd/template.c b/dmd/template.c
index bcde5ce0..589ab88f 100644
--- a/dmd/template.c
+++ b/dmd/template.c
@@ -647,7 +647,7 @@ int TemplateDeclaration::leastAsSpecialized(TemplateDeclaration *td2)
* 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 @@ MATCH TemplateDeclaration::deduceMatch(Objects *targsi, Expressions *fargs,
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 @@ int TemplateDeclaration::isOverloadable()
* 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 *TemplateDeclaration::deduce(Scope *sc, Loc loc,
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 @@ FuncDeclaration *TemplateDeclaration::deduce(Scope *sc, Loc loc,
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;
diff --git a/dmd/template.h b/dmd/template.h
index e2be61c0..9051e337 100644
--- a/dmd/template.h
+++ b/dmd/template.h
@@ -71,8 +71,8 @@ struct TemplateDeclaration : ScopeDsymbol
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; }
diff --git a/lphobos/phobos.d b/lphobos/phobos.d
index 7c5818ca..a322d073 100644
--- a/lphobos/phobos.d
+++ b/lphobos/phobos.d
@@ -2,16 +2,18 @@ module phobos;
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,
diff --git a/lphobos/std/base64.d b/lphobos/std/base64.d
new file mode 100644
index 00000000..a907374d
--- /dev/null
+++ b/lphobos/std/base64.d
@@ -0,0 +1,293 @@
+/**
+ * Encodes/decodes MIME base64 data.
+ *
+ * Macros:
+ * WIKI=Phobos/StdBase64
+ * References:
+ * Wikipedia Base64$(BR)
+ * RFC 2045$(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!");
+}
+