From 4428e47a665082f20a11cf1f8930a671b3e36d09 Mon Sep 17 00:00:00 2001 From: Tomas Lindquist Olsen Date: Fri, 28 Dec 2007 23:52:40 +0100 Subject: [PATCH] [svn r134] Merged the DMD 1.024 frontend. Added std.base64. --- dmd/declaration.c | 5 + dmd/declaration.h | 2 + dmd/doc.c | 19 ++- dmd/expression.c | 22 ++-- dmd/func.c | 3 + dmd/mars.c | 2 +- dmd/module.c | 1 + dmd/module.h | 1 + dmd/mtype.c | 60 +++++---- dmd/opover.c | 2 +- dmd/template.c | 12 +- dmd/template.h | 4 +- lphobos/phobos.d | 4 +- lphobos/std/base64.d | 293 +++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 381 insertions(+), 49 deletions(-) create mode 100644 lphobos/std/base64.d 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!"); +} +