# HG changeset patch # User lindquist # Date 1195532960 -3600 # Node ID 288fe1029e1f50ea2a2d895d23ee3f81e5cd9941 # Parent 3efbcc81ba45670b4f07948e70468f32ade7376b [svn r112] Fixed 'case 1,2,3:' style case statements. Fixed a bunch of bugs with return/break/continue in loops. Fixed support for the DMDFE hidden implicit return value variable. This can be needed for some foreach statements where the loop body is converted to a nested delegate, but also possibly returns from the function. Added std.math to phobos. Added AA runtime support code, done ground work for implementing AAs. Several other bugfixes. diff -r 3efbcc81ba45 -r 288fe1029e1f dmd/expression.c --- a/dmd/expression.c Tue Nov 20 00:02:35 2007 +0100 +++ b/dmd/expression.c Tue Nov 20 05:29:20 2007 +0100 @@ -5870,6 +5870,7 @@ { VarExp *dve = (VarExp *)e1; FuncDeclaration *f = dve->var->isFuncDeclaration(); + VarDeclaration *v = dve->var->isVarDeclaration(); if (f && f->isNested()) { Expression *e; @@ -5878,6 +5879,10 @@ e = e->semantic(sc); return e; } + else if (v) + { + v->llvmNeedsStorage = true; + } } else if (e1->op == TOKarray) { @@ -7543,6 +7548,7 @@ AddExp::AddExp(Loc loc, Expression *e1, Expression *e2) : BinExp(loc, TOKadd, sizeof(AddExp), e1, e2) { + llvmFieldIndex = false; } Expression *AddExp::semantic(Scope *sc) diff -r 3efbcc81ba45 -r 288fe1029e1f dmd/expression.h --- a/dmd/expression.h Tue Nov 20 00:02:35 2007 +0100 +++ b/dmd/expression.h Tue Nov 20 05:29:20 2007 +0100 @@ -1121,6 +1121,9 @@ Identifier *opId_r(); elem *toElem(IRState *irs); + + // LLVMDC + bool llvmFieldIndex; }; struct MinExp : BinExp diff -r 3efbcc81ba45 -r 288fe1029e1f dmd/mtype.c --- a/dmd/mtype.c Tue Nov 20 00:02:35 2007 +0100 +++ b/dmd/mtype.c Tue Nov 20 05:29:20 2007 +0100 @@ -4309,6 +4309,11 @@ b = new AddrExp(e->loc, e); b->type = e->type->pointerTo(); b = new AddExp(e->loc, b, new IntegerExp(e->loc, v->offset, Type::tint32)); +#if IN_LLVM + // LLVMDC modification + // this is *essential* + ((AddExp*)b)->llvmFieldIndex = true; +#endif b->type = v->type->pointerTo(); e = new PtrExp(e->loc, b); e->type = v->type; diff -r 3efbcc81ba45 -r 288fe1029e1f dmd/statement.c --- a/dmd/statement.c Tue Nov 20 00:02:35 2007 +0100 +++ b/dmd/statement.c Tue Nov 20 05:29:20 2007 +0100 @@ -1437,7 +1437,7 @@ exps->push(aggr); size_t keysize = taa->key->size(); keysize = (keysize + 3) & ~3; - exps->push(new IntegerExp(0, keysize, Type::tint32)); + exps->push(new IntegerExp(0, keysize, Type::tsize_t)); exps->push(flde); e = new CallExp(loc, ec, exps); e->type = Type::tindex; // don't run semantic() on e diff -r 3efbcc81ba45 -r 288fe1029e1f dmd/statement.h --- a/dmd/statement.h Tue Nov 20 00:02:35 2007 +0100 +++ b/dmd/statement.h Tue Nov 20 05:29:20 2007 +0100 @@ -46,6 +46,7 @@ struct TryCatchStatement; struct HdrGenState; struct InterState; +struct CaseStatement; enum TOK; @@ -114,6 +115,7 @@ virtual CompoundStatement *isCompoundStatement() { return NULL; } virtual ReturnStatement *isReturnStatement() { return NULL; } virtual IfStatement *isIfStatement() { return NULL; } + virtual CaseStatement* isCaseStatement() { return NULL; } }; struct ExpStatement : Statement @@ -436,6 +438,8 @@ Statement *inlineScan(InlineScanState *iss); void toIR(IRState *irs); + + CaseStatement* isCaseStatement() { return this; } }; struct DefaultStatement : Statement diff -r 3efbcc81ba45 -r 288fe1029e1f gen/arrays.cpp --- a/gen/arrays.cpp Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/arrays.cpp Tue Nov 20 05:29:20 2007 +0100 @@ -369,7 +369,7 @@ return ret; } -void DtoArrayCopy(DSliceValue* dst, DSliceValue* src) +void DtoArrayCopySlices(DSliceValue* dst, DSliceValue* src) { llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty); @@ -390,6 +390,25 @@ new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb()); } +void DtoArrayCopyToSlice(DSliceValue* dst, DValue* src) +{ + llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty); + + llvm::Value* sz1; + llvm::Value* dstarr = new llvm::BitCastInst(get_slice_ptr(dst,sz1),arrty,"tmp",gIR->scopebb()); + llvm::Value* srcarr = new llvm::BitCastInst(DtoArrayPtr(src),arrty,"tmp",gIR->scopebb()); + + llvm::Function* fn = (global.params.is64bit) ? LLVM_DeclareMemCpy64() : LLVM_DeclareMemCpy32(); + std::vector llargs; + llargs.resize(4); + llargs[0] = dstarr; + llargs[1] = srcarr; + llargs[2] = sz1; + llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false); + + new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb()); +} + ////////////////////////////////////////////////////////////////////////////////////////// void DtoStaticArrayCopy(llvm::Value* dst, llvm::Value* src) { diff -r 3efbcc81ba45 -r 288fe1029e1f gen/arrays.h --- a/gen/arrays.h Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/arrays.h Tue Nov 20 05:29:20 2007 +0100 @@ -10,7 +10,8 @@ llvm::Constant* DtoConstSlice(llvm::Constant* dim, llvm::Constant* ptr); llvm::Constant* DtoConstStaticArray(const llvm::Type* t, llvm::Constant* c); -void DtoArrayCopy(DSliceValue* dst, DSliceValue* src); +void DtoArrayCopySlices(DSliceValue* dst, DSliceValue* src); +void DtoArrayCopyToSlice(DSliceValue* dst, DValue* src); void DtoArrayInit(llvm::Value* l, llvm::Value* r); void DtoArrayInit(llvm::Value* ptr, llvm::Value* dim, llvm::Value* val); diff -r 3efbcc81ba45 -r 288fe1029e1f gen/functions.cpp --- a/gen/functions.cpp Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/functions.cpp Tue Nov 20 05:29:20 2007 +0100 @@ -378,6 +378,8 @@ } if (f->llvmUsesThis) { iarg->setName("this"); + fdecl->llvmThisVar = iarg; + assert(fdecl->llvmThisVar); ++iarg; } int varargs = -1; @@ -426,6 +428,8 @@ if (fd->llvmDefined) return; fd->llvmDefined = true; + assert(fd->llvmDeclared); + Logger::println("DtoDefineFunc(%s)", fd->toPrettyChars()); LOG_SCOPE; @@ -474,6 +478,7 @@ assert(fd->llvmIRFunc); gIR->functions.push_back(fd->llvmIRFunc); + /* // moved to declaration // this handling if (f->llvmUsesThis) { Logger::println("uses this"); @@ -483,6 +488,7 @@ fd->llvmThisVar = func->arg_begin(); assert(fd->llvmThisVar != 0); } + */ if (fd->isMain()) gIR->emitMain = true; @@ -497,6 +503,12 @@ f->llvmAllocaPoint = new llvm::BitCastInst(llvm::ConstantInt::get(llvm::Type::Int32Ty,0,false),llvm::Type::Int32Ty,"alloca point",gIR->scopebb()); gIR->func()->allocapoint = f->llvmAllocaPoint; + // need result variable? (not nested) + if (fd->vresult && !fd->vresult->nestedref) { + Logger::println("non-nested vresult value"); + fd->vresult->llvmValue = new llvm::AllocaInst(DtoType(fd->vresult->type),"function_vresult",f->llvmAllocaPoint); + } + // give arguments storage size_t n = Argument::dim(f->parameters); for (int i=0; i < n; ++i) { @@ -524,7 +536,14 @@ llvm::Value* parentNested = NULL; if (FuncDeclaration* fd2 = fd->toParent()->isFuncDeclaration()) { - parentNested = fd2->llvmNested; + if (!fd->isStatic()) + parentNested = fd2->llvmNested; + } + + // need result variable? (nested) + if (fd->vresult && fd->vresult->nestedref) { + Logger::println("nested vresult value: %s", fd->vresult->toChars()); + fd->llvmNestedVars.insert(fd->vresult); } // construct nested variables struct diff -r 3efbcc81ba45 -r 288fe1029e1f gen/irstate.cpp --- a/gen/irstate.cpp Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/irstate.cpp Tue Nov 20 05:29:20 2007 +0100 @@ -83,10 +83,6 @@ llvm::BasicBlock* IRState::scopebb() { - return scopebegin(); -} -llvm::BasicBlock* IRState::scopebegin() -{ IRScope& s = scope(); assert(s.begin); return s.begin; diff -r 3efbcc81ba45 -r 288fe1029e1f gen/irstate.h --- a/gen/irstate.h Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/irstate.h Tue Nov 20 05:29:20 2007 +0100 @@ -158,9 +158,8 @@ // basic block scopes std::vector scopes; IRScope& scope(); - llvm::BasicBlock* scopebegin(); + llvm::BasicBlock* scopebb(); llvm::BasicBlock* scopeend(); - llvm::BasicBlock* scopebb(); bool scopereturned(); // loop blocks diff -r 3efbcc81ba45 -r 288fe1029e1f gen/statements.cpp --- a/gen/statements.cpp Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/statements.cpp Tue Nov 20 05:29:20 2007 +0100 @@ -163,7 +163,7 @@ Logger::cout() << "if conditional: " << *cond_val << '\n'; cond_val = DtoBoolean(cond_val); } - llvm::Value* ifgoback = new llvm::BranchInst(ifbb, elsebb, cond_val, gIR->scopebegin()); + llvm::Value* ifgoback = new llvm::BranchInst(ifbb, elsebb, cond_val, gIR->scopebb()); // replace current scope gIR->scope() = IRScope(ifbb,elsebb); @@ -171,7 +171,7 @@ // do scoped statements ifbody->toIR(p); if (!gIR->scopereturned()) { - new llvm::BranchInst(endbb,gIR->scopebegin()); + new llvm::BranchInst(endbb,gIR->scopebb()); } if (elsebody) { @@ -179,7 +179,7 @@ gIR->scope() = IRScope(elsebb,endbb); elsebody->toIR(p); if (!gIR->scopereturned()) { - new llvm::BranchInst(endbb,gIR->scopebegin()); + new llvm::BranchInst(endbb,gIR->scopebb()); } } @@ -199,14 +199,14 @@ llvm::BasicBlock* beginbb = 0; // remove useless branches by clearing and reusing the current basicblock - llvm::BasicBlock* bb = p->scopebegin(); + llvm::BasicBlock* bb = p->scopebb(); if (bb->empty()) { beginbb = bb; } else { assert(!p->scopereturned()); beginbb = new llvm::BasicBlock("scope", p->topfunc(), oldend); - new llvm::BranchInst(beginbb, p->scopebegin()); + new llvm::BranchInst(beginbb, p->scopebb()); } llvm::BasicBlock* endbb = new llvm::BasicBlock("endscope", p->topfunc(), oldend); @@ -234,7 +234,7 @@ // move into the while block p->ir->CreateBr(whilebb); - //new llvm::BranchInst(whilebb, gIR->scopebegin()); + //new llvm::BranchInst(whilebb, gIR->scopebb()); // replace current scope gIR->scope() = IRScope(whilebb,endbb); @@ -256,7 +256,8 @@ p->loopbbs.pop_back(); // loop - new llvm::BranchInst(whilebb, gIR->scopebegin()); + if (!gIR->scopereturned()) + new llvm::BranchInst(whilebb, gIR->scopebb()); // rewrite the scope gIR->scope() = IRScope(endbb,oldend); @@ -276,13 +277,16 @@ llvm::BasicBlock* endbb = new llvm::BasicBlock("enddowhile", gIR->topfunc(), oldend); // move into the while block - new llvm::BranchInst(dowhilebb, gIR->scopebegin()); + assert(!gIR->scopereturned()); + new llvm::BranchInst(dowhilebb, gIR->scopebb()); // replace current scope gIR->scope() = IRScope(dowhilebb,endbb); // do-while body code + p->loopbbs.push_back(IRScope(dowhilebb,endbb)); body->toIR(p); + p->loopbbs.pop_back(); // create the condition DValue* cond_e = condition->toElem(p); @@ -290,7 +294,7 @@ delete cond_e; // conditional branch - llvm::Value* ifbreak = new llvm::BranchInst(dowhilebb, endbb, cond_val, gIR->scopebegin()); + llvm::Value* ifbreak = new llvm::BranchInst(dowhilebb, endbb, cond_val, gIR->scopebb()); // rewrite the scope gIR->scope() = IRScope(endbb,oldend); @@ -316,7 +320,7 @@ init->toIR(p); // move into the for condition block, ie. start the loop - new llvm::BranchInst(forbb, gIR->scopebegin()); + new llvm::BranchInst(forbb, gIR->scopebb()); p->loopbbs.push_back(IRScope(forincbb,endbb)); @@ -338,7 +342,8 @@ body->toIR(p); // move into the for increment block - new llvm::BranchInst(forincbb, gIR->scopebegin()); + if (!gIR->scopereturned()) + new llvm::BranchInst(forincbb, gIR->scopebb()); gIR->scope() = IRScope(forincbb, endbb); // increment @@ -348,7 +353,8 @@ } // loop - new llvm::BranchInst(forbb, gIR->scopebegin()); + if (!gIR->scopereturned()) + new llvm::BranchInst(forbb, gIR->scopebb()); p->loopbbs.pop_back(); @@ -368,7 +374,7 @@ assert(0); } else { - new llvm::BranchInst(gIR->loopbbs.back().end, gIR->scopebegin()); + new llvm::BranchInst(gIR->loopbbs.back().end, gIR->scopebb()); } } @@ -384,7 +390,7 @@ assert(0); } else { - new llvm::BranchInst(gIR->loopbbs.back().begin, gIR->scopebegin()); + new llvm::BranchInst(gIR->loopbbs.back().begin, gIR->scopebb()); } } @@ -527,25 +533,33 @@ llvm::BasicBlock* oldend = gIR->scopeend(); // collect the needed cases - typedef std::pair CasePair; + typedef std::pair > CasePair; std::vector vcases; + std::vector vbodies; for (int i=0; idim; ++i) { CaseStatement* cs = (CaseStatement*)cases->data[i]; - // get the case value - DValue* e = cs->exp->toElem(p); - DConstValue* ce = e->isConst(); - assert(ce); - llvm::ConstantInt* ec = isaConstantInt(ce->c); - assert(ec); - delete e; - // create the case bb with a nice label std::string lblname("case"+std::string(cs->exp->toChars())); llvm::BasicBlock* bb = new llvm::BasicBlock(lblname, p->topfunc(), oldend); - vcases.push_back(CasePair(bb,ec)); + std::vector tmp; + CaseStatement* last; + do { + // get the case value + DValue* e = cs->exp->toElem(p); + DConstValue* ce = e->isConst(); + assert(ce); + llvm::ConstantInt* ec = isaConstantInt(ce->c); + assert(ec); + tmp.push_back(ec); + last = cs; + } + while (cs = cs->statement->isCaseStatement()); + + vcases.push_back(CasePair(bb, tmp)); + vbodies.push_back(last->statement); } // default @@ -566,7 +580,11 @@ size_t n = vcases.size(); for (size_t i=0; iaddCase(vcases[i].second, vcases[i].first); + size_t nc = vcases[i].second.size(); + for (size_t j=0; jaddCase(vcases[i].second[j], vcases[i].first); + } } // insert case statements @@ -576,7 +594,7 @@ p->scope() = IRScope(vcases[i].first,nextbb); p->loopbbs.push_back(IRScope(p->scopebb(),endbb)); - static_cast(cases->data[i])->statement->toIR(p); + vbodies[i]->toIR(p); p->loopbbs.pop_back(); llvm::BasicBlock* curbb = p->scopebb(); @@ -606,6 +624,15 @@ } ////////////////////////////////////////////////////////////////////////////// +void CaseStatement::toIR(IRState* p) +{ + Logger::println("CaseStatement::toIR(): %s", toChars()); + LOG_SCOPE; + + assert(0); +} + +////////////////////////////////////////////////////////////////////////////// void UnrolledLoopStatement::toIR(IRState* p) { @@ -735,7 +762,7 @@ } new llvm::BranchInst(bodybb, endbb, done, p->scopebb()); - // body + // init body p->scope() = IRScope(bodybb,nextbb); // get value for this iteration @@ -750,12 +777,10 @@ DValue* dst = new DVarValue(value->type, valvar, true); DValue* src = new DVarValue(value->type, value->llvmValue, true); DtoAssign(dst, src); - delete dst; - delete src; value->llvmValue = valvar; } - // body + // emit body p->loopbbs.push_back(IRScope(nextbb,endbb)); body->toIR(p); p->loopbbs.pop_back(); @@ -860,7 +885,7 @@ //STUBST(ReturnStatement); //STUBST(ContinueStatement); STUBST(DefaultStatement); -STUBST(CaseStatement); +//STUBST(CaseStatement); //STUBST(SwitchStatement); STUBST(SwitchErrorStatement); STUBST(Statement); diff -r 3efbcc81ba45 -r 288fe1029e1f gen/toir.cpp --- a/gen/toir.cpp Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/toir.cpp Tue Nov 20 05:29:20 2007 +0100 @@ -488,7 +488,9 @@ DtoAssign(l, r); } - return l; + if (l->isSlice() || l->isComplex()) + return l; + return new DImValue(type, l->getRVal()); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -507,7 +509,8 @@ Type* e2type = DtoDType(e2->type); if (e1type != e2type) { - if (e1type->ty == Tpointer && e1next && e1next->ty == Tstruct) { + if (llvmFieldIndex) { + assert(e1type->ty == Tpointer && e1next && e1next->ty == Tstruct); Logger::println("add to AddrExp of struct"); assert(r->isConst()); llvm::ConstantInt* cofs = llvm::cast(r->isConst()->c); @@ -1001,8 +1004,9 @@ llargs[j] = DtoArgument(llfnty->getParamType(j), fnarg, (Expression*)arguments->data[i]); // this hack is necessary :/ if (dfn && dfn->func && dfn->func->llvmRunTimeHack) { - Logger::println("llvmRunTimeHack==true"); + Logger::println("llvmRunTimeHack==true - force casting argument"); if (llargs[j]->getType() != llfnty->getParamType(j)) { + Logger::cout() << "from: " << *llargs[j]->getType() << " to: " << *llfnty->getParamType(j); llargs[j] = DtoBitCast(llargs[j], llfnty->getParamType(j)); } } @@ -1155,12 +1159,15 @@ DValue* AddrExp::toElem(IRState* p) { - Logger::print("AddrExp::toElem: %s | %s\n", toChars(), type->toChars()); + Logger::println("AddrExp::toElem: %s | %s", toChars(), type->toChars()); LOG_SCOPE; DValue* v = e1->toElem(p); - if (v->isField()) + if (v->isField()) { + Logger::println("is field"); return v; + } else if (DFuncValue* fv = v->isFunc()) { + Logger::println("is func"); //Logger::println("FuncDeclaration"); FuncDeclaration* fd = fv->func; assert(fd); @@ -1169,8 +1176,10 @@ return new DFuncValue(fd, fd->llvmValue); } else if (DImValue* im = v->isIm()) { + Logger::println("is immediate"); return v; } + Logger::println("is nothing special"); return new DFieldValue(type, v->getLVal(), false); } @@ -1178,7 +1187,7 @@ DValue* PtrExp::toElem(IRState* p) { - Logger::print("PtrExp::toElem: %s | %s\n", toChars(), type->toChars()); + Logger::println("PtrExp::toElem: %s | %s", toChars(), type->toChars()); LOG_SCOPE; DValue* a = e1->toElem(p); @@ -1552,7 +1561,7 @@ Type* t = DtoDType(e1->type); Type* e2t = DtoDType(e2->type); - assert(t == e2t); + //assert(t == e2t); llvm::Value* eval = 0; @@ -1571,7 +1580,12 @@ default: assert(0); } - eval = new llvm::ICmpInst(cmpop, l->getRVal(), r->getRVal(), "tmp", p->scopebb()); + llvm::Value* lv = l->getRVal(); + llvm::Value* rv = r->getRVal(); + if (rv->getType() != lv->getType()) { + rv = DtoBitCast(rv, lv->getType()); + } + eval = new llvm::ICmpInst(cmpop, lv, rv, "tmp", p->scopebb()); } else if (t->iscomplex()) { @@ -2212,7 +2226,7 @@ if (fd->isNested()) Logger::println("nested"); Logger::println("kind = %s\n", fd->kind()); - DtoForceDeclareDsymbol(fd); + DtoForceDefineDsymbol(fd); bool temp = false; llvm::Value* lval = NULL; diff -r 3efbcc81ba45 -r 288fe1029e1f gen/tollvm.cpp --- a/gen/tollvm.cpp Tue Nov 20 00:02:35 2007 +0100 +++ b/gen/tollvm.cpp Tue Nov 20 05:29:20 2007 +0100 @@ -174,6 +174,13 @@ return DtoType(bt); } + // associative arrays + case Taarray: + { + // TODO this is a kludge + return llvm::PointerType::get(llvm::Type::Int8Ty); + } + default: printf("trying to convert unknown type with value %d\n", t->ty); assert(0); @@ -796,7 +803,15 @@ // on this stack if (fd == f) { - llvm::Value* v = DtoGEPi(vd->llvmValue,0,unsigned(vd->llvmNestedIndex),"tmp"); + llvm::Value* vdv = vd->llvmValue; + if (!vdv) + { + Logger::println(":o null vd->llvmValue for: %s", vd->toChars()); + vdv = fd->llvmNested; + assert(vdv); + } + assert(vd->llvmNestedIndex != ~0); + llvm::Value* v = DtoGEPi(vdv,0,unsigned(vd->llvmNestedIndex),"tmp"); if (vd->isParameter() && (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type))) { Logger::cout() << "1267 loading: " << *v << '\n'; v = gIR->ir->CreateLoad(v,"tmp"); @@ -859,7 +874,7 @@ // lhs is slice if (DSliceValue* s = lhs->isSlice()) { if (DSliceValue* s2 = rhs->isSlice()) { - DtoArrayCopy(s, s2); + DtoArrayCopySlices(s, s2); } else if (t->next == t2) { if (s->len) @@ -867,8 +882,9 @@ else DtoArrayInit(s->ptr, rhs->getRVal()); } - else - assert(rhs->inPlace()); + else { + DtoArrayCopyToSlice(s, rhs); + } } // rhs is slice else if (DSliceValue* s = rhs->isSlice()) { @@ -926,8 +942,15 @@ DtoComplexAssign(dst, rhs->getRVal()); } else { + llvm::Value* l; + if (DLRValue* lr = lhs->isLRValue()) { + l = lr->getLVal(); + rhs = DtoCast(rhs, lr->getLType()); + } + else { + l = lhs->getLVal(); + } llvm::Value* r = rhs->getRVal(); - llvm::Value* l = lhs->getLVal(); Logger::cout() << "assign\nlhs: " << *l << "rhs: " << *r << '\n'; const llvm::Type* lit = l->getType()->getContainedType(0); if (r->getType() != lit) { // :( @@ -1124,6 +1147,7 @@ DValue* DtoCast(DValue* val, Type* to) { Type* fromtype = DtoDType(val->getType()); + Logger::println("Casting from '%s' to '%s'", fromtype->toChars(), to->toChars()); if (fromtype->isintegral()) { return DtoCastInt(val, to); } diff -r 3efbcc81ba45 -r 288fe1029e1f llvmdc.kdevelop.filelist --- a/llvmdc.kdevelop.filelist Tue Nov 20 00:02:35 2007 +0100 +++ b/llvmdc.kdevelop.filelist Tue Nov 20 05:29:20 2007 +0100 @@ -142,6 +142,7 @@ lphobos/internal lphobos/internal/aApply.d lphobos/internal/aApplyR.d +lphobos/internal/aaA.d lphobos/internal/adi.d lphobos/internal/arrays.d lphobos/internal/cmath2.d @@ -181,6 +182,7 @@ lphobos/std/ctype.d lphobos/std/format.d lphobos/std/intrinsic.d +lphobos/std/math.d lphobos/std/moduleinit.d lphobos/std/outofmemory.d lphobos/std/stdarg.d @@ -229,6 +231,7 @@ runalltests.d test test/a.d +test/aa1.d test/alignment.d test/alloca1.d test/arrayinit.d @@ -310,6 +313,9 @@ test/bug66.d test/bug7.d test/bug70.d +test/bug71.d +test/bug72.d +test/bug73.d test/bug8.d test/bug9.d test/c.d @@ -401,6 +407,7 @@ test/structs6.d test/structs7.d test/switch1.d +test/switch2.d test/sync1.d test/templ1.d test/templ2.d diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/build.sh --- a/lphobos/build.sh Tue Nov 20 00:02:35 2007 +0100 +++ b/lphobos/build.sh Tue Nov 20 05:29:20 2007 +0100 @@ -20,7 +20,7 @@ echo "compiling typeinfo 1" -rebuild typeinfos1.d -c -oqobj -dc=llvmdc-posix -v || exit 1 +rebuild typeinfos1.d -c -oqobj -dc=llvmdc-posix || exit 1 llvm-link -f -o=../lib/llvmdcore.bc `ls obj/typeinfo1.*.bc` ../lib/llvmdcore.bc || exit 1 echo "compiling typeinfo 2" @@ -33,10 +33,12 @@ llvm-link -f -o=../lib/llvmdcore.bc obj/aApply.bc obj/aApplyR.bc ../lib/llvmdcore.bc || exit 1 echo "compiling array runtime support" -llvmdc internal/qsort2.d -c -odobj || exit +llvmdc internal/qsort2.d -c -odobj || exit 1 llvm-link -f -o=../lib/llvmdcore.bc obj/qsort2.bc ../lib/llvmdcore.bc || exit 1 -llvmdc internal/adi.d -c -odobj || exit +llvmdc internal/adi.d -c -odobj || exit 1 llvm-link -f -o=../lib/llvmdcore.bc obj/adi.bc ../lib/llvmdcore.bc || exit 1 +llvmdc internal/aaA.d -c -odobj || exit 1 +llvm-link -f -o=../lib/llvmdcore.bc obj/aaA.bc ../lib/llvmdcore.bc || exit 1 echo "compiling object implementation" llvmdc internal/objectimpl.d -c -odobj || exit 1 diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/internal/aaA.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/internal/aaA.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,821 @@ +//_ aaA.d + +/** + * Part of the D programming language runtime library. + * Implementation of associative arrays. + */ + +/* + * Copyright (C) 2000-2007 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * 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: + * + * o 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. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + + +//import std.stdio; +import std.c.stdarg; +import std.c.stdio; +import std.c.stdlib; +import std.c.string; +import std.string; + +import std.outofmemory; + +// Auto-rehash and pre-allocate - Dave Fladebo + +static size_t[] prime_list = [ + 97UL, 389UL, + 1543UL, 6151UL, + 24593UL, 98317UL, + 393241UL, 1572869UL, + 6291469UL, 25165843UL, + 100663319UL, 402653189UL, + 1610612741UL, 4294967291UL +]; + +/* This is the type of the return value for dynamic arrays. + * It should be a type that is returned in registers. + * Although DMD will return types of Array in registers, + * gcc will not, so we instead use a 'long'. + */ +alias long ArrayRet_t; + +struct Array +{ + size_t length; + void* ptr; +} + +struct aaA +{ + aaA *left; + aaA *right; + hash_t hash; + /* key */ + /* value */ +} + +struct BB +{ + aaA*[] b; + size_t nodes; // total number of aaA nodes +} + +/* This is the type actually seen by the programmer, although + * it is completely opaque. + */ + +struct AA +{ + BB* a; +} + +/********************************** + * Align to next pointer boundary, so that + * GC won't be faced with misaligned pointers + * in value. + */ + +size_t aligntsize(size_t tsize) +{ + // Is pointer alignment on the x64 4 bytes or 8? + return (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1); +} + +extern (C): + +/************************************************* + * Invariant for aa. + */ + +/+ +void _aaInvAh(aaA*[] aa) +{ + for (size_t i = 0; i < aa.length; i++) + { + if (aa[i]) + _aaInvAh_x(aa[i]); + } +} + +private int _aaCmpAh_x(aaA *e1, aaA *e2) +{ int c; + + c = e1.hash - e2.hash; + if (c == 0) + { + c = e1.key.length - e2.key.length; + if (c == 0) + c = memcmp((char *)e1.key, (char *)e2.key, e1.key.length); + } + return c; +} + +private void _aaInvAh_x(aaA *e) +{ + hash_t key_hash; + aaA *e1; + aaA *e2; + + key_hash = getHash(e.key); + assert(key_hash == e.hash); + + while (1) + { int c; + + e1 = e.left; + if (e1) + { + _aaInvAh_x(e1); // ordinary recursion + do + { + c = _aaCmpAh_x(e1, e); + assert(c < 0); + e1 = e1.right; + } while (e1 != null); + } + + e2 = e.right; + if (e2) + { + do + { + c = _aaCmpAh_x(e, e2); + assert(c < 0); + e2 = e2.left; + } while (e2 != null); + e = e.right; // tail recursion + } + else + break; + } +} ++/ + +/**************************************************** + * Determine number of entries in associative array. + */ + +size_t _aaLen(AA aa) + in + { + //printf("_aaLen()+\n"); + //_aaInv(aa); + } + out (result) + { + size_t len = 0; + + void _aaLen_x(aaA* ex) + { + auto e = ex; + len++; + + while (1) + { + if (e.right) + _aaLen_x(e.right); + e = e.left; + if (!e) + break; + len++; + } + } + + if (aa.a) + { + foreach (e; aa.a.b) + { + if (e) + _aaLen_x(e); + } + } + assert(len == result); + + //printf("_aaLen()-\n"); + } + body + { + return aa.a ? aa.a.nodes : 0; + } + + +/************************************************* + * Get pointer to value in associative array indexed by key. + * Add entry for key if it is not already there. + */ + +void* _aaGet(AA* aa, TypeInfo keyti, size_t valuesize, ...) + in + { + assert(aa); + } + out (result) + { + assert(result); + assert(aa.a); + assert(aa.a.b.length); + //assert(_aaInAh(*aa.a, key)); + } + body + { + auto pkey = cast(void *)(&valuesize + 1); + size_t i; + aaA* e; + auto keysize = aligntsize(keyti.tsize()); + + if (!aa.a) + aa.a = new BB(); + + if (!aa.a.b.length) + { + alias aaA *pa; + auto len = prime_list[0]; + + aa.a.b = new pa[len]; + } + + auto key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + i = key_hash % aa.a.b.length; + auto pe = &aa.a.b[i]; + while ((e = *pe) != null) + { + if (key_hash == e.hash) + { + auto c = keyti.compare(pkey, e + 1); + if (c == 0) + goto Lret; + pe = (c < 0) ? &e.left : &e.right; + } + else + pe = (key_hash < e.hash) ? &e.left : &e.right; + } + + // Not found, create new elem + //printf("create new one\n"); + e = cast(aaA *) cast(void*) new void[aaA.sizeof + keysize + valuesize]; + memcpy(e + 1, pkey, keysize); + e.hash = key_hash; + *pe = e; + + auto nodes = ++aa.a.nodes; + //printf("length = %d, nodes = %d\n", (*aa.a).length, nodes); + if (nodes > aa.a.b.length * 4) + { + _aaRehash(aa,keyti); + } + + Lret: + return cast(void *)(e + 1) + keysize; + } + + +/************************************************* + * Get pointer to value in associative array indexed by key. + * Returns null if it is not already there. + */ + +void* _aaGetRvalue(AA aa, TypeInfo keyti, size_t valuesize, ...) + { + //printf("_aaGetRvalue(valuesize = %u)\n", valuesize); + if (!aa.a) + return null; + + auto pkey = cast(void *)(&valuesize + 1); + auto keysize = aligntsize(keyti.tsize()); + auto len = aa.a.b.length; + + if (len) + { + auto key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + size_t i = key_hash % len; + auto e = aa.a.b[i]; + while (e != null) + { + if (key_hash == e.hash) + { + auto c = keyti.compare(pkey, e + 1); + if (c == 0) + return cast(void *)(e + 1) + keysize; + e = (c < 0) ? e.left : e.right; + } + else + e = (key_hash < e.hash) ? e.left : e.right; + } + } + return null; // not found, caller will throw exception + } + + +/************************************************* + * Determine if key is in aa. + * Returns: + * null not in aa + * !=null in aa, return pointer to value + */ + +void* _aaIn(AA aa, TypeInfo keyti, ...) + in + { + } + out (result) + { + //assert(result == 0 || result == 1); + } + body + { + if (aa.a) + { + auto pkey = cast(void *)(&keyti + 1); + + //printf("_aaIn(), .length = %d, .ptr = %x\n", aa.a.length, cast(uint)aa.a.ptr); + auto len = aa.a.b.length; + + if (len) + { + auto key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + size_t i = key_hash % len; + auto e = aa.a.b[i]; + while (e != null) + { + if (key_hash == e.hash) + { + auto c = keyti.compare(pkey, e + 1); + if (c == 0) + return cast(void *)(e + 1) + aligntsize(keyti.tsize()); + e = (c < 0) ? e.left : e.right; + } + else + e = (key_hash < e.hash) ? e.left : e.right; + } + } + } + + // Not found + return null; + } + + +/************************************************* + * Delete key entry in aa[]. + * If key is not in aa[], do nothing. + */ + +void _aaDel(AA aa, TypeInfo keyti, ...) + { + auto pkey = cast(void *)(&keyti + 1); + aaA* e; + + if (aa.a && aa.a.b.length) + { + auto key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + size_t i = key_hash % aa.a.b.length; + auto pe = &aa.a.b[i]; + while ((e = *pe) != null) // null means not found + { + if (key_hash == e.hash) + { + auto c = keyti.compare(pkey, e + 1); + if (c == 0) + { + if (!e.left && !e.right) + { + *pe = null; + } + else if (e.left && !e.right) + { + *pe = e.left; + e.left = null; + } + else if (!e.left && e.right) + { + *pe = e.right; + e.right = null; + } + else + { + *pe = e.left; + e.left = null; + do + pe = &(*pe).right; + while (*pe); + *pe = e.right; + e.right = null; + } + + aa.a.nodes--; + + // Should notify GC that e can be free'd now + break; + } + pe = (c < 0) ? &e.left : &e.right; + } + else + pe = (key_hash < e.hash) ? &e.left : &e.right; + } + } + } + + +/******************************************** + * Produce array of values from aa. + */ + +ArrayRet_t _aaValues(AA aa, size_t keysize, size_t valuesize) + in + { + assert(keysize == aligntsize(keysize)); + } + body + { + size_t resi; + Array a; + + void _aaValues_x(aaA* e) + { + do + { + memcpy(a.ptr + resi * valuesize, + cast(byte*)e + aaA.sizeof + keysize, + valuesize); + resi++; + if (e.left) + { if (!e.right) + { e = e.left; + continue; + } + _aaValues_x(e.left); + } + e = e.right; + } while (e != null); + } + + if (aa.a) + { + a.length = _aaLen(aa); + a.ptr = (new void[a.length * valuesize]).ptr; + resi = 0; + foreach (e; aa.a.b) + { + if (e) + _aaValues_x(e); + } + assert(resi == a.length); + } + return *cast(ArrayRet_t*)(&a); + } + + +/******************************************** + * Rehash an array. + */ + +void* _aaRehash(AA* paa, TypeInfo keyti) + in + { + //_aaInvAh(paa); + } + out (result) + { + //_aaInvAh(result); + } + body + { + BB newb; + + void _aaRehash_x(aaA* olde) + { + while (1) + { + auto left = olde.left; + auto right = olde.right; + olde.left = null; + olde.right = null; + + aaA* e; + + //printf("rehash %p\n", olde); + auto key_hash = olde.hash; + size_t i = key_hash % newb.b.length; + auto pe = &newb.b[i]; + while ((e = *pe) != null) + { + //printf("\te = %p, e.left = %p, e.right = %p\n", e, e.left, e.right); + assert(e.left != e); + assert(e.right != e); + if (key_hash == e.hash) + { + auto c = keyti.compare(olde + 1, e + 1); + assert(c != 0); + pe = (c < 0) ? &e.left : &e.right; + } + else + pe = (key_hash < e.hash) ? &e.left : &e.right; + } + *pe = olde; + + if (right) + { + if (!left) + { olde = right; + continue; + } + _aaRehash_x(right); + } + if (!left) + break; + olde = left; + } + } + + //printf("Rehash\n"); + if (paa.a) + { + auto aa = paa.a; + auto len = _aaLen(*paa); + if (len) + { size_t i; + + for (i = 0; i < prime_list.length - 1; i++) + { + if (len <= prime_list[i]) + break; + } + len = prime_list[i]; + newb.b = new aaA*[len]; + + foreach (e; aa.b) + { + if (e) + _aaRehash_x(e); + } + + newb.nodes = aa.nodes; + } + + *paa.a = newb; + } + return (*paa).a; + } + + +/******************************************** + * Produce array of N byte keys from aa. + */ + +ArrayRet_t _aaKeys(AA aa, size_t keysize) + { + byte[] res; + size_t resi; + + void _aaKeys_x(aaA* e) + { + do + { + memcpy(&res[resi * keysize], cast(byte*)(e + 1), keysize); + resi++; + if (e.left) + { if (!e.right) + { e = e.left; + continue; + } + _aaKeys_x(e.left); + } + e = e.right; + } while (e != null); + } + + auto len = _aaLen(aa); + if (!len) + return 0; + res = cast(byte[])new void[len * keysize]; + resi = 0; + foreach (e; aa.a.b) + { + if (e) + _aaKeys_x(e); + } + assert(resi == len); + + Array a; + a.length = len; + a.ptr = res.ptr; + return *cast(ArrayRet_t*)(&a); + } + + +/********************************************** + * 'apply' for associative arrays - to support foreach + */ + +// dg is D, but _aaApply() is C +extern (D) typedef int delegate(void *) dg_t; + +int _aaApply(AA aa, size_t keysize, dg_t dg) +in +{ + assert(aligntsize(keysize) == keysize); +} +body +{ int result; + + //printf("_aaApply(aa = x%llx, keysize = %d, dg = x%llx)\n", aa.a, keysize, dg); + + int treewalker(aaA* e) + { int result; + + do + { + //printf("treewalker(e = %p, dg = x%llx)\n", e, dg); + result = dg(cast(void *)(e + 1) + keysize); + if (result) + break; + if (e.right) + { if (!e.left) + { + e = e.right; + continue; + } + result = treewalker(e.right); + if (result) + break; + } + e = e.left; + } while (e); + + return result; + } + + if (aa.a) + { + foreach (e; aa.a.b) + { + if (e) + { + result = treewalker(e); + if (result) + break; + } + } + } + return result; +} + +// dg is D, but _aaApply2() is C +extern (D) typedef int delegate(void *, void *) dg2_t; + +int _aaApply2(AA aa, size_t keysize, dg2_t dg) +in +{ + assert(aligntsize(keysize) == keysize); +} +body +{ int result; + + //printf("_aaApply(aa = x%llx, keysize = %d, dg = x%llx)\n", aa.a, keysize, dg); + + int treewalker(aaA* e) + { int result; + + do + { + //printf("treewalker(e = %p, dg = x%llx)\n", e, dg); + result = dg(cast(void *)(e + 1), cast(void *)(e + 1) + keysize); + if (result) + break; + if (e.right) + { if (!e.left) + { + e = e.right; + continue; + } + result = treewalker(e.right); + if (result) + break; + } + e = e.left; + } while (e); + + return result; + } + + if (aa.a) + { + foreach (e; aa.a.b) + { + if (e) + { + result = treewalker(e); + if (result) + break; + } + } + } + return result; +} + + +/*********************************** + * Construct an associative array of type ti from + * length pairs of key/value pairs. + */ + +extern (C) +BB* _d_assocarrayliteralT(TypeInfo_AssociativeArray ti, size_t length, ...) +{ + auto valuesize = ti.next.tsize(); // value size + auto keyti = ti.key; + auto keysize = keyti.tsize(); // key size + BB* result; + + //printf("_d_assocarrayliteralT(keysize = %d, valuesize = %d, length = %d)\n", keysize, valuesize, length); + //writefln("tivalue = %s", ti.next.classinfo.name); + if (length == 0 || valuesize == 0 || keysize == 0) + { + ; + } + else + { + va_list q; + va_start!(size_t)(q, length); + + result = new BB(); + size_t i; + + for (i = 0; i < prime_list.length - 1; i++) + { + if (length <= prime_list[i]) + break; + } + auto len = prime_list[i]; + result.b = new aaA*[len]; + + size_t keystacksize = (keysize + int.sizeof - 1) & ~(int.sizeof - 1); + size_t valuestacksize = (valuesize + int.sizeof - 1) & ~(int.sizeof - 1); + + size_t keytsize = aligntsize(keysize); + + for (size_t j = 0; j < length; j++) + { void* pkey = q; + q += keystacksize; + void* pvalue = q; + q += valuestacksize; + aaA* e; + + auto key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + i = key_hash % len; + auto pe = &result.b[i]; + while (1) + { + e = *pe; + if (!e) + { + // Not found, create new elem + //printf("create new one\n"); + e = cast(aaA *) cast(void*) new void[aaA.sizeof + keytsize + valuesize]; + memcpy(e + 1, pkey, keysize); + e.hash = key_hash; + *pe = e; + result.nodes++; + break; + } + if (key_hash == e.hash) + { + auto c = keyti.compare(pkey, e + 1); + if (c == 0) + break; + pe = (c < 0) ? &e.left : &e.right; + } + else + pe = (key_hash < e.hash) ? &e.left : &e.right; + } + memcpy(cast(void *)(e + 1) + keytsize, pvalue, valuesize); + } + + va_end(q); + } + return result; +} + + diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/phobos.d --- a/lphobos/phobos.d Tue Nov 20 00:02:35 2007 +0100 +++ b/lphobos/phobos.d Tue Nov 20 05:29:20 2007 +0100 @@ -4,6 +4,7 @@ std.array, std.ctype, std.intrinsic, +std.math, std.moduleinit, std.outofmemory, std.stdint, @@ -12,6 +13,9 @@ std.uni, std.utf, +//std.format, +//std.string, + std.c.fenv, std.c.locale, std.c.math, diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/std/format.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/std/format.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,1563 @@ + +// Written in the D programming language. + +/** + * This module implements the workhorse functionality for string and I/O formatting. + * It's comparable to C99's vsprintf(). + * + * Macros: + * WIKI = Phobos/StdFormat + */ + +/* + * Copyright (C) 2004-2006 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * Modified for LLVMDC by Tomas Lindquist Olsen + * + * 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: + * + * o 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. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.format; + +//debug=format; // uncomment to turn on debugging printf's + +import std.stdarg; // caller will need va_list + +private import std.utf; +private import std.c.stdlib; +private import std.c.string; +private import std.string; + +version (Windows) +{ + version (DigitalMars) + { + version = DigitalMarsC; + } +} + +version (DigitalMarsC) +{ + // This is DMC's internal floating point formatting function + extern (C) + { + extern char* function(int c, int flags, int precision, real* pdval, + char* buf, int* psl, int width) __pfloatfmt; + } +} +else +{ + // Use C99 snprintf + extern (C) int snprintf(char* s, size_t n, char* format, ...); +} + +/********************************************************************** + * Signals a mismatch between a format and its corresponding argument. + */ +class FormatError : Error +{ + private: + + this() + { + super("std.format"); + } + + this(char[] msg) + { + super("std.format " ~ msg); + } +} + + +enum Mangle : char +{ + Tvoid = 'v', + Tbool = 'b', + Tbyte = 'g', + Tubyte = 'h', + Tshort = 's', + Tushort = 't', + Tint = 'i', + Tuint = 'k', + Tlong = 'l', + Tulong = 'm', + Tfloat = 'f', + Tdouble = 'd', + Treal = 'e', + + Tifloat = 'o', + Tidouble = 'p', + Tireal = 'j', + Tcfloat = 'q', + Tcdouble = 'r', + Tcreal = 'c', + + Tchar = 'a', + Twchar = 'u', + Tdchar = 'w', + + Tarray = 'A', + Tsarray = 'G', + Taarray = 'H', + Tpointer = 'P', + Tfunction = 'F', + Tident = 'I', + Tclass = 'C', + Tstruct = 'S', + Tenum = 'E', + Ttypedef = 'T', + Tdelegate = 'D', + + Tconst = 'x', + Tinvariant = 'y', +} + +// return the TypeInfo for a primitive type and null otherwise. +// This is required since for arrays of ints we only have the mangled +// char to work from. If arrays always subclassed TypeInfo_Array this +// routine could go away. +private TypeInfo primitiveTypeInfo(Mangle m) +{ + TypeInfo ti; + + switch (m) + { + case Mangle.Tvoid: + ti = typeid(void);break; + case Mangle.Tbool: + ti = typeid(bool);break; + case Mangle.Tbyte: + ti = typeid(byte);break; + case Mangle.Tubyte: + ti = typeid(ubyte);break; + case Mangle.Tshort: + ti = typeid(short);break; + case Mangle.Tushort: + ti = typeid(ushort);break; + case Mangle.Tint: + ti = typeid(int);break; + case Mangle.Tuint: + ti = typeid(uint);break; + case Mangle.Tlong: + ti = typeid(long);break; + case Mangle.Tulong: + ti = typeid(ulong);break; + case Mangle.Tfloat: + ti = typeid(float);break; + case Mangle.Tdouble: + ti = typeid(double);break; + case Mangle.Treal: + ti = typeid(real);break; + case Mangle.Tifloat: + ti = typeid(ifloat);break; + case Mangle.Tidouble: + ti = typeid(idouble);break; + case Mangle.Tireal: + ti = typeid(ireal);break; + /+ + // No complex in LLVMDC yes + case Mangle.Tcfloat: + ti = typeid(cfloat);break; + case Mangle.Tcdouble: + ti = typeid(cdouble);break; + case Mangle.Tcreal: + ti = typeid(creal);break; + +/ + case Mangle.Tchar: + ti = typeid(char);break; + case Mangle.Twchar: + ti = typeid(wchar);break; + case Mangle.Tdchar: + ti = typeid(dchar); + default: + ti = null; + } + return ti; +} + +/************************************ + * Interprets variadic argument list pointed to by argptr whose types are given + * by arguments[], formats them according to embedded format strings in the + * variadic argument list, and sends the resulting characters to putc. + * + * The variadic arguments are consumed in order. + * Each is formatted into a sequence of chars, using the default format + * specification for its type, and the + * characters are sequentially passed to putc. + * If a char[], wchar[], or dchar[] + * argument is encountered, it is interpreted as a format string. As many + * arguments as specified in the format string are consumed and formatted + * according to the format specifications in that string and passed to putc. If + * there are too few remaining arguments, a FormatError is thrown. If there are + * more remaining arguments than needed by the format specification, the default + * processing of arguments resumes until they are all consumed. + * + * Params: + * putc = Output is sent do this delegate, character by character. + * arguments = Array of TypeInfo's, one for each argument to be formatted. + * argptr = Points to variadic argument list. + * + * Throws: + * Mismatched arguments and formats result in a FormatError being thrown. + * + * Format_String: + * $(I Format strings) + * consist of characters interspersed with + * $(I format specifications). Characters are simply copied + * to the output (such as putc) after any necessary conversion + * to the corresponding UTF-8 sequence. + * + * A $(I format specification) starts with a '%' character, + * and has the following grammar: + +
+$(I FormatSpecification):
+    $(B '%%')
+    $(B '%') $(I Flags) $(I Width) $(I Precision) $(I FormatChar)
+
+$(I Flags):
+    $(I empty)
+    $(B '-') $(I Flags)
+    $(B '+') $(I Flags)
+    $(B '#') $(I Flags)
+    $(B '0') $(I Flags)
+    $(B ' ') $(I Flags)
+
+$(I Width):
+    $(I empty)
+    $(I Integer)
+    $(B '*')
+
+$(I Precision):
+    $(I empty)
+    $(B '.')
+    $(B '.') $(I Integer)
+    $(B '.*')
+
+$(I Integer):
+    $(I Digit)
+    $(I Digit) $(I Integer)
+
+$(I Digit):
+    $(B '0')
+    $(B '1')
+    $(B '2')
+    $(B '3')
+    $(B '4')
+    $(B '5')
+    $(B '6')
+    $(B '7')
+    $(B '8')
+    $(B '9')
+
+$(I FormatChar):
+    $(B 's')
+    $(B 'b')
+    $(B 'd')
+    $(B 'o')
+    $(B 'x')
+    $(B 'X')
+    $(B 'e')
+    $(B 'E')
+    $(B 'f')
+    $(B 'F')
+    $(B 'g')
+    $(B 'G')
+    $(B 'a')
+    $(B 'A')
+
+
+
$(I Flags) +
+
$(B '-') +
+ Left justify the result in the field. + It overrides any $(B 0) flag. + +
$(B '+') +
Prefix positive numbers in a signed conversion with a $(B +). + It overrides any $(I space) flag. + +
$(B '#') +
Use alternative formatting: +
+
For $(B 'o'): +
Add to precision as necessary so that the first digit + of the octal formatting is a '0', even if both the argument + and the $(I Precision) are zero. +
For $(B 'x') ($(B 'X')): +
If non-zero, prefix result with $(B 0x) ($(B 0X)). +
For floating point formatting: +
Always insert the decimal point. +
For $(B 'g') ($(B 'G')): +
Do not elide trailing zeros. +
+ +
$(B '0') +
For integer and floating point formatting when not nan or + infinity, use leading zeros + to pad rather than spaces. + Ignore if there's a $(I Precision). + +
$(B ' ') +
Prefix positive numbers in a signed conversion with a space. +
+ +
$(I Width) +
+ Specifies the minimum field width. + If the width is a $(B *), the next argument, which must be + of type $(B int), is taken as the width. + If the width is negative, it is as if the $(B -) was given + as a $(I Flags) character. + +
$(I Precision) +
Gives the precision for numeric conversions. + If the precision is a $(B *), the next argument, which must be + of type $(B int), is taken as the precision. If it is negative, + it is as if there was no $(I Precision). + +
$(I FormatChar) +
+
+
$(B 's') +
The corresponding argument is formatted in a manner consistent + with its type: +
+
$(B bool) +
The result is 'true' or 'false'. +
integral types +
The $(B %d) format is used. +
floating point types +
The $(B %g) format is used. +
string types +
The result is the string converted to UTF-8. + A $(I Precision) specifies the maximum number of characters + to use in the result. +
classes derived from $(B Object) +
The result is the string returned from the class instance's + $(B .toString()) method. + A $(I Precision) specifies the maximum number of characters + to use in the result. +
non-string static and dynamic arrays +
The result is [s0, s1, ...] + where sk is the kth element + formatted with the default format. +
+ +
$(B 'b','d','o','x','X') +
The corresponding argument must be an integral type + and is formatted as an integer. If the argument is a signed type + and the $(I FormatChar) is $(B d) it is converted to + a signed string of characters, otherwise it is treated as + unsigned. An argument of type $(B bool) is formatted as '1' + or '0'. The base used is binary for $(B b), octal for $(B o), + decimal + for $(B d), and hexadecimal for $(B x) or $(B X). + $(B x) formats using lower case letters, $(B X) uppercase. + If there are fewer resulting digits than the $(I Precision), + leading zeros are used as necessary. + If the $(I Precision) is 0 and the number is 0, no digits + result. + +
$(B 'e','E') +
A floating point number is formatted as one digit before + the decimal point, $(I Precision) digits after, the $(I FormatChar), + ±, followed by at least a two digit exponent: $(I d.dddddd)e$(I ±dd). + If there is no $(I Precision), six + digits are generated after the decimal point. + If the $(I Precision) is 0, no decimal point is generated. + +
$(B 'f','F') +
A floating point number is formatted in decimal notation. + The $(I Precision) specifies the number of digits generated + after the decimal point. It defaults to six. At least one digit + is generated before the decimal point. If the $(I Precision) + is zero, no decimal point is generated. + +
$(B 'g','G') +
A floating point number is formatted in either $(B e) or + $(B f) format for $(B g); $(B E) or $(B F) format for + $(B G). + The $(B f) format is used if the exponent for an $(B e) format + is greater than -5 and less than the $(I Precision). + The $(I Precision) specifies the number of significant + digits, and defaults to six. + Trailing zeros are elided after the decimal point, if the fractional + part is zero then no decimal point is generated. + +
$(B 'a','A') +
A floating point number is formatted in hexadecimal + exponential notation 0x$(I h.hhhhhh)p$(I ±d). + There is one hexadecimal digit before the decimal point, and as + many after as specified by the $(I Precision). + If the $(I Precision) is zero, no decimal point is generated. + If there is no $(I Precision), as many hexadecimal digits as + necessary to exactly represent the mantissa are generated. + The exponent is written in as few digits as possible, + but at least one, is in decimal, and represents a power of 2 as in + $(I h.hhhhhh)*2$(I ±d). + The exponent for zero is zero. + The hexadecimal digits, x and p are in upper case if the + $(I FormatChar) is upper case. +
+ + Floating point NaN's are formatted as $(B nan) if the + $(I FormatChar) is lower case, or $(B NAN) if upper. + Floating point infinities are formatted as $(B inf) or + $(B infinity) if the + $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper. +
+ +Example: + +------------------------- +import std.c.stdio; +import std.format; + +void formattedPrint(...) +{ + void putc(char c) + { + fputc(c, stdout); + } + + std.format.doFormat(&putc, _arguments, _argptr); +} + +... + +int x = 27; +// prints 'The answer is 27:6' +formattedPrint("The answer is %s:", x, 6); +------------------------ + */ + +void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) +{ int j; + TypeInfo ti; + Mangle m; + uint flags; + int field_width; + int precision; + + enum : uint + { + FLdash = 1, + FLplus = 2, + FLspace = 4, + FLhash = 8, + FLlngdbl = 0x20, + FL0pad = 0x40, + FLprecision = 0x80, + } + + static TypeInfo skipCI(TypeInfo valti) + { + while (1) + { + if (valti.classinfo.name.length == 18 && + valti.classinfo.name[9..18] == "Invariant") + valti = (cast(TypeInfo_Invariant)valti).next; + else if (valti.classinfo.name.length == 14 && + valti.classinfo.name[9..14] == "Const") + valti = (cast(TypeInfo_Const)valti).next; + else + break; + } + return valti; + } + + void formatArg(char fc) + { + bool vbit; + ulong vnumber; + char vchar; + dchar vdchar; + Object vobject; + real vreal; + creal vcreal; + Mangle m2; + int signed = 0; + uint base = 10; + int uc; + char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary + char* prefix = ""; + string s; + + void putstr(char[] s) + { + //printf("flags = x%x\n", flags); + int prepad = 0; + int postpad = 0; + int padding = field_width - (strlen(prefix) + s.length); + if (padding > 0) + { + if (flags & FLdash) + postpad = padding; + else + prepad = padding; + } + + if (flags & FL0pad) + { + while (*prefix) + putc(*prefix++); + while (prepad--) + putc('0'); + } + else + { + while (prepad--) + putc(' '); + while (*prefix) + putc(*prefix++); + } + + foreach (dchar c; s) + putc(c); + + while (postpad--) + putc(' '); + } + + void putreal(real v) + { + //printf("putreal %Lg\n", vreal); + + switch (fc) + { + case 's': + fc = 'g'; + break; + + case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': + break; + + default: + //printf("fc = '%c'\n", fc); + Lerror: + throw new FormatError("floating"); + } + version (DigitalMarsC) + { + int sl; + char[] fbuf = tmpbuf; + if (!(flags & FLprecision)) + precision = 6; + while (1) + { + sl = fbuf.length; + prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, + precision, &v, cast(char*)fbuf, &sl, field_width); + if (sl != -1) + break; + sl = fbuf.length * 2; + fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; + } + putstr(fbuf[0 .. sl]); + } + else + { + int sl; + char[] fbuf = tmpbuf; + char[12] format; + format[0] = '%'; + int i = 1; + if (flags & FLdash) + format[i++] = '-'; + if (flags & FLplus) + format[i++] = '+'; + if (flags & FLspace) + format[i++] = ' '; + if (flags & FLhash) + format[i++] = '#'; + if (flags & FL0pad) + format[i++] = '0'; + format[i + 0] = '*'; + format[i + 1] = '.'; + format[i + 2] = '*'; + format[i + 3] = 'L'; + format[i + 4] = fc; + format[i + 5] = 0; + if (!(flags & FLprecision)) + precision = -1; + while (1) + { int n; + + sl = fbuf.length; + n = snprintf(fbuf.ptr, sl, format.ptr, field_width, precision, v); + //printf("format = '%s', n = %d\n", cast(char*)format, n); + if (n >= 0 && n < sl) + { sl = n; + break; + } + if (n < 0) + sl = sl * 2; + else + sl = n + 1; + fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; + } + putstr(fbuf[0 .. sl]); + } + return; + } + + static Mangle getMan(TypeInfo ti) + { + auto m = cast(Mangle)ti.classinfo.name[9]; + if (ti.classinfo.name.length == 20 && + ti.classinfo.name[9..20] == "StaticArray") + m = cast(Mangle)'G'; + return m; + } + + void putArray(void* p, size_t len, TypeInfo valti) + { + //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize()); + putc('['); + valti = skipCI(valti); + size_t tsize = valti.tsize(); + auto argptrSave = argptr; + auto tiSave = ti; + auto mSave = m; + ti = valti; + //printf("\n%.*s\n", valti.classinfo.name); + m = getMan(valti); + while (len--) + { + //doFormat(putc, (&valti)[0 .. 1], p); + argptr = p; + formatArg('s'); + + p += tsize; + if (len > 0) putc(','); + } + m = mSave; + ti = tiSave; + argptr = argptrSave; + putc(']'); + } + + void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) + { + putc('['); + bool comma=false; + auto argptrSave = argptr; + auto tiSave = ti; + auto mSave = m; + valti = skipCI(valti); + keyti = skipCI(keyti); + foreach(inout fakevalue; vaa) + { + if (comma) putc(','); + comma = true; + // the key comes before the value + ubyte* key = &fakevalue - long.sizeof; + + //doFormat(putc, (&keyti)[0..1], key); + argptr = key; + ti = keyti; + m = getMan(keyti); + formatArg('s'); + + putc(':'); + auto keysize = keyti.tsize; + keysize = (keysize + 3) & ~3; + ubyte* value = key + keysize; + //doFormat(putc, (&valti)[0..1], value); + argptr = value; + ti = valti; + m = getMan(valti); + formatArg('s'); + } + m = mSave; + ti = tiSave; + argptr = argptrSave; + putc(']'); + } + + //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); + switch (m) + { + case Mangle.Tbool: + vbit = va_arg!(bool)(argptr); + if (fc != 's') + { vnumber = vbit; + goto Lnumber; + } + putstr(vbit ? "true" : "false"); + return; + + + case Mangle.Tchar: + vchar = va_arg!(char)(argptr); + if (fc != 's') + { vnumber = vchar; + goto Lnumber; + } + L2: + putstr((&vchar)[0 .. 1]); + return; + + case Mangle.Twchar: + vdchar = va_arg!(wchar)(argptr); + goto L1; + + case Mangle.Tdchar: + vdchar = va_arg!(dchar)(argptr); + L1: + if (fc != 's') + { vnumber = vdchar; + goto Lnumber; + } + if (vdchar <= 0x7F) + { vchar = cast(char)vdchar; + goto L2; + } + else + { if (!isValidDchar(vdchar)) + throw new UtfException("invalid dchar in format", 0); + char[4] vbuf; + putstr(toUTF8(vbuf, vdchar)); + } + return; + + + case Mangle.Tbyte: + signed = 1; + vnumber = va_arg!(byte)(argptr); + goto Lnumber; + + case Mangle.Tubyte: + vnumber = va_arg!(ubyte)(argptr); + goto Lnumber; + + case Mangle.Tshort: + signed = 1; + vnumber = va_arg!(short)(argptr); + goto Lnumber; + + case Mangle.Tushort: + vnumber = va_arg!(ushort)(argptr); + goto Lnumber; + + case Mangle.Tint: + signed = 1; + vnumber = va_arg!(int)(argptr); + goto Lnumber; + + case Mangle.Tuint: + Luint: + vnumber = va_arg!(uint)(argptr); + goto Lnumber; + + case Mangle.Tlong: + signed = 1; + vnumber = cast(ulong)va_arg!(long)(argptr); + goto Lnumber; + + case Mangle.Tulong: + Lulong: + vnumber = va_arg!(ulong)(argptr); + goto Lnumber; + + case Mangle.Tclass: + vobject = va_arg!(Object)(argptr); + if (vobject is null) + s = "null"; + else + s = vobject.toString(); + goto Lputstr; + + case Mangle.Tpointer: + vnumber = cast(ulong)va_arg!(void*)(argptr); + uc = 1; + flags |= FL0pad; + if (!(flags & FLprecision)) + { flags |= FLprecision; + precision = (void*).sizeof; + } + base = 16; + goto Lnumber; + + + case Mangle.Tfloat: + case Mangle.Tifloat: + if (fc == 'x' || fc == 'X') + goto Luint; + vreal = va_arg!(float)(argptr); + goto Lreal; + + case Mangle.Tdouble: + case Mangle.Tidouble: + if (fc == 'x' || fc == 'X') + goto Lulong; + vreal = va_arg!(double)(argptr); + goto Lreal; + + case Mangle.Treal: + case Mangle.Tireal: + vreal = va_arg!(real)(argptr); + goto Lreal; + + + case Mangle.Tcfloat: + vcreal = va_arg!(cfloat)(argptr); + goto Lcomplex; + + case Mangle.Tcdouble: + vcreal = va_arg!(cdouble)(argptr); + goto Lcomplex; + + case Mangle.Tcreal: + vcreal = va_arg!(creal)(argptr); + goto Lcomplex; + + case Mangle.Tsarray: + putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next); + return; + + case Mangle.Tarray: + int mi = 10; + if (ti.classinfo.name.length == 14 && + ti.classinfo.name[9..14] == "Array") + { // array of non-primitive types + TypeInfo tn = (cast(TypeInfo_Array)ti).next; + tn = skipCI(tn); + switch (cast(Mangle)tn.classinfo.name[9]) + { + case Mangle.Tchar: goto LarrayChar; + case Mangle.Twchar: goto LarrayWchar; + case Mangle.Tdchar: goto LarrayDchar; + default: + break; + } + void[] va = va_arg!(void[])(argptr); + putArray(va.ptr, va.length, tn); + return; + } + if (ti.classinfo.name.length == 25 && + ti.classinfo.name[9..25] == "AssociativeArray") + { // associative array + ubyte[long] vaa = va_arg!(ubyte[long])(argptr); + putAArray(vaa, + (cast(TypeInfo_AssociativeArray)ti).next, + (cast(TypeInfo_AssociativeArray)ti).key); + return; + } + + while (1) + { + m2 = cast(Mangle)ti.classinfo.name[mi]; + switch (m2) + { + case Mangle.Tchar: + LarrayChar: + s = va_arg!(char[])(argptr); + goto Lputstr; + + case Mangle.Twchar: + LarrayWchar: + wchar[] sw = va_arg!(wchar[])(argptr); + s = toUTF8(sw); + goto Lputstr; + + case Mangle.Tdchar: + LarrayDchar: + dchar[] sd = va_arg!(dchar[])(argptr); + s = toUTF8(sd); + Lputstr: + if (fc != 's') + throw new FormatError("string"); + if (flags & FLprecision && precision < s.length) + s = s[0 .. precision]; + putstr(s); + break; + + case Mangle.Tconst: + case Mangle.Tinvariant: + mi++; + continue; + + default: + TypeInfo ti2 = primitiveTypeInfo(m2); + if (!ti2) + goto Lerror; + void[] va = va_arg!(void[])(argptr); + putArray(va.ptr, va.length, ti2); + } + return; + } + + case Mangle.Ttypedef: + ti = (cast(TypeInfo_Typedef)ti).base; + m = cast(Mangle)ti.classinfo.name[9]; + formatArg(fc); + return; + + case Mangle.Tenum: + ti = (cast(TypeInfo_Enum)ti).base; + m = cast(Mangle)ti.classinfo.name[9]; + formatArg(fc); + return; + + case Mangle.Tstruct: + { TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; + if (tis.xtoString is null) + throw new FormatError("Can't convert " ~ tis.toString() ~ " to string: \"string toString()\" not defined"); + s = tis.xtoString(argptr); + argptr += (tis.tsize() + 3) & ~3; + goto Lputstr; + } + + default: + goto Lerror; + } + + Lnumber: + switch (fc) + { + case 's': + case 'd': + if (signed) + { if (cast(long)vnumber < 0) + { prefix = "-"; + vnumber = -vnumber; + } + else if (flags & FLplus) + prefix = "+"; + else if (flags & FLspace) + prefix = " "; + } + break; + + case 'b': + signed = 0; + base = 2; + break; + + case 'o': + signed = 0; + base = 8; + break; + + case 'X': + uc = 1; + if (flags & FLhash && vnumber) + prefix = "0X"; + signed = 0; + base = 16; + break; + + case 'x': + if (flags & FLhash && vnumber) + prefix = "0x"; + signed = 0; + base = 16; + break; + + default: + goto Lerror; + } + + if (!signed) + { + switch (m) + { + case Mangle.Tbyte: + vnumber &= 0xFF; + break; + + case Mangle.Tshort: + vnumber &= 0xFFFF; + break; + + case Mangle.Tint: + vnumber &= 0xFFFFFFFF; + break; + + default: + break; + } + } + + if (flags & FLprecision && fc != 'p') + flags &= ~FL0pad; + + if (vnumber < base) + { + if (vnumber == 0 && precision == 0 && flags & FLprecision && + !(fc == 'o' && flags & FLhash)) + { + putstr(null); + return; + } + if (precision == 0 || !(flags & FLprecision)) + { vchar = cast(char)('0' + vnumber); + if (vnumber < 10) + vchar = cast(char)('0' + vnumber); + else + vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); + goto L2; + } + } + + int n = tmpbuf.length; + char c; + int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); + + while (vnumber) + { + c = cast(char)((vnumber % base) + '0'); + if (c > '9') + c += hexoffset; + vnumber /= base; + tmpbuf[--n] = c; + } + if (tmpbuf.length - n < precision && precision < tmpbuf.length) + { + int m = tmpbuf.length - precision; + tmpbuf[m .. n] = '0'; + n = m; + } + else if (flags & FLhash && fc == 'o') + prefix = "0"; + putstr(tmpbuf[n .. tmpbuf.length]); + return; + + Lreal: + putreal(vreal); + return; + + Lcomplex: + putreal(vcreal.re); + putc('+'); + putreal(vcreal.im); + putc('i'); + return; + + Lerror: + throw new FormatError("formatArg"); + } + + + for (j = 0; j < arguments.length; ) + { ti = arguments[j++]; + //printf("test1: '%.*s' %d\n", ti.classinfo.name, ti.classinfo.name.length); + //ti.print(); + + flags = 0; + precision = 0; + field_width = 0; + + ti = skipCI(ti); + int mi = 9; + do + { + if (ti.classinfo.name.length <= mi) + goto Lerror; + m = cast(Mangle)ti.classinfo.name[mi++]; + } while (m == Mangle.Tconst || m == Mangle.Tinvariant); + + if (m == Mangle.Tarray) + { + if (ti.classinfo.name.length == 14 && + ti.classinfo.name[9..14] == "Array") + { + TypeInfo tn = (cast(TypeInfo_Array)ti).next; + tn = skipCI(tn); + switch (cast(Mangle)tn.classinfo.name[9]) + { + case Mangle.Tchar: + case Mangle.Twchar: + case Mangle.Tdchar: + ti = tn; + mi = 9; + break; + default: + break; + } + } + L1: + Mangle m2 = cast(Mangle)ti.classinfo.name[mi]; + string fmt; // format string + wstring wfmt; + dstring dfmt; + + /* For performance reasons, this code takes advantage of the + * fact that most format strings will be ASCII, and that the + * format specifiers are always ASCII. This means we only need + * to deal with UTF in a couple of isolated spots. + */ + + switch (m2) + { + case Mangle.Tchar: + fmt = va_arg!(char[])(argptr); + break; + + case Mangle.Twchar: + wfmt = va_arg!(wchar[])(argptr); + fmt = toUTF8(wfmt); + break; + + case Mangle.Tdchar: + dfmt = va_arg!(dchar[])(argptr); + fmt = toUTF8(dfmt); + break; + + case Mangle.Tconst: + case Mangle.Tinvariant: + mi++; + goto L1; + + default: + formatArg('s'); + continue; + } + + for (size_t i = 0; i < fmt.length; ) + { dchar c = fmt[i++]; + + dchar getFmtChar() + { // Valid format specifier characters will never be UTF + if (i == fmt.length) + throw new FormatError("invalid specifier"); + return fmt[i++]; + } + + int getFmtInt() + { int n; + + while (1) + { + n = n * 10 + (c - '0'); + if (n < 0) // overflow + throw new FormatError("int overflow"); + c = getFmtChar(); + if (c < '0' || c > '9') + break; + } + return n; + } + + int getFmtStar() + { Mangle m; + TypeInfo ti; + + if (j == arguments.length) + throw new FormatError("too few arguments"); + ti = arguments[j++]; + m = cast(Mangle)ti.classinfo.name[9]; + if (m != Mangle.Tint) + throw new FormatError("int argument expected"); + return va_arg!(int)(argptr); + } + + if (c != '%') + { + if (c > 0x7F) // if UTF sequence + { + i--; // back up and decode UTF sequence + c = std.utf.decode(fmt, i); + } + Lputc: + putc(c); + continue; + } + + // Get flags {-+ #} + flags = 0; + while (1) + { + c = getFmtChar(); + switch (c) + { + case '-': flags |= FLdash; continue; + case '+': flags |= FLplus; continue; + case ' ': flags |= FLspace; continue; + case '#': flags |= FLhash; continue; + case '0': flags |= FL0pad; continue; + + case '%': if (flags == 0) + goto Lputc; + default: break; + } + break; + } + + // Get field width + field_width = 0; + if (c == '*') + { + field_width = getFmtStar(); + if (field_width < 0) + { flags |= FLdash; + field_width = -field_width; + } + + c = getFmtChar(); + } + else if (c >= '0' && c <= '9') + field_width = getFmtInt(); + + if (flags & FLplus) + flags &= ~FLspace; + if (flags & FLdash) + flags &= ~FL0pad; + + // Get precision + precision = 0; + if (c == '.') + { flags |= FLprecision; + //flags &= ~FL0pad; + + c = getFmtChar(); + if (c == '*') + { + precision = getFmtStar(); + if (precision < 0) + { precision = 0; + flags &= ~FLprecision; + } + + c = getFmtChar(); + } + else if (c >= '0' && c <= '9') + precision = getFmtInt(); + } + + if (j == arguments.length) + goto Lerror; + ti = arguments[j++]; + ti = skipCI(ti); + mi = 9; + do + { + m = cast(Mangle)ti.classinfo.name[mi++]; + } while (m == Mangle.Tconst || m == Mangle.Tinvariant); + + if (c > 0x7F) // if UTF sequence + goto Lerror; // format specifiers can't be UTF + formatArg(cast(char)c); + } + } + else + { + formatArg('s'); + } + } + return; + +Lerror: + throw new FormatError(); +} + +/* ======================== Unit Tests ====================================== */ + +unittest +{ + int i; + string s; + + debug(format) printf("std.format.format.unittest\n"); + + s = std.string.format("hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo"); + assert(s == "hello world! true 57 1000000000x foo"); + + s = std.string.format(1.67, " %A ", -1.28, float.nan); + /* The host C library is used to format floats. + * C99 doesn't specify what the hex digit before the decimal point + * is for %A. + */ + version (linux) + assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan"); + else + assert(s == "1.67 -0X1.47AE147AE147BP+0 nan"); + + s = std.string.format("%x %X", 0x1234AF, 0xAFAFAFAF); + assert(s == "1234af AFAFAFAF"); + + s = std.string.format("%b %o", 0x1234AF, 0xAFAFAFAF); + assert(s == "100100011010010101111 25753727657"); + + s = std.string.format("%d %s", 0x1234AF, 0xAFAFAFAF); + assert(s == "1193135 2947526575"); + + s = std.string.format("%s", 1.2 + 3.4i); + assert(s == "1.2+3.4i"); + + s = std.string.format("%x %X", 1.32, 6.78f); + assert(s == "3ff51eb851eb851f 40D8F5C3"); + + s = std.string.format("%#06.*f",2,12.345); + assert(s == "012.35"); + + s = std.string.format("%#0*.*f",6,2,12.345); + assert(s == "012.35"); + + s = std.string.format("%7.4g:", 12.678); + assert(s == " 12.68:"); + + s = std.string.format("%7.4g:", 12.678L); + assert(s == " 12.68:"); + + s = std.string.format("%04f|%05d|%#05x|%#5x",-4.,-10,1,1); + assert(s == "-4.000000|-0010|0x001| 0x1"); + + i = -10; + s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(s == "-10|-10|-10|-10|-10.0000"); + + i = -5; + s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(s == "-5| -5|-05|-5|-5.0000"); + + i = 0; + s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(s == "0| 0|000|0|0.0000"); + + i = 5; + s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(s == "5| 5|005|5|5.0000"); + + i = 10; + s = std.string.format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(s == "10| 10|010|10|10.0000"); + + s = std.string.format("%.0d", 0); + assert(s == ""); + + s = std.string.format("%.g", .34); + assert(s == "0.3"); + + s = std.string.format("%.0g", .34); + assert(s == "0.3"); + + s = std.string.format("%.2g", .34); + assert(s == "0.34"); + + s = std.string.format("%0.0008f", 1e-08); + assert(s == "0.00000001"); + + s = std.string.format("%0.0008f", 1e-05); + assert(s == "0.00001000"); + + s = "helloworld"; + string r; + r = std.string.format("%.2s", s[0..5]); + assert(r == "he"); + r = std.string.format("%.20s", s[0..5]); + assert(r == "hello"); + r = std.string.format("%8s", s[0..5]); + assert(r == " hello"); + + byte[] arrbyte = new byte[4]; + arrbyte[0] = 100; + arrbyte[1] = -99; + arrbyte[3] = 0; + r = std.string.format(arrbyte); + assert(r == "[100,-99,0,0]"); + + ubyte[] arrubyte = new ubyte[4]; + arrubyte[0] = 100; + arrubyte[1] = 200; + arrubyte[3] = 0; + r = std.string.format(arrubyte); + assert(r == "[100,200,0,0]"); + + short[] arrshort = new short[4]; + arrshort[0] = 100; + arrshort[1] = -999; + arrshort[3] = 0; + r = std.string.format(arrshort); + assert(r == "[100,-999,0,0]"); + r = std.string.format("%s",arrshort); + assert(r == "[100,-999,0,0]"); + + ushort[] arrushort = new ushort[4]; + arrushort[0] = 100; + arrushort[1] = 20_000; + arrushort[3] = 0; + r = std.string.format(arrushort); + assert(r == "[100,20000,0,0]"); + + int[] arrint = new int[4]; + arrint[0] = 100; + arrint[1] = -999; + arrint[3] = 0; + r = std.string.format(arrint); + assert(r == "[100,-999,0,0]"); + r = std.string.format("%s",arrint); + assert(r == "[100,-999,0,0]"); + + long[] arrlong = new long[4]; + arrlong[0] = 100; + arrlong[1] = -999; + arrlong[3] = 0; + r = std.string.format(arrlong); + assert(r == "[100,-999,0,0]"); + r = std.string.format("%s",arrlong); + assert(r == "[100,-999,0,0]"); + + ulong[] arrulong = new ulong[4]; + arrulong[0] = 100; + arrulong[1] = 999; + arrulong[3] = 0; + r = std.string.format(arrulong); + assert(r == "[100,999,0,0]"); + + string[] arr2 = new string[4]; + arr2[0] = "hello"; + arr2[1] = "world"; + arr2[3] = "foo"; + r = std.string.format(arr2); + assert(r == "[hello,world,,foo]"); + + r = std.string.format("%.8d", 7); + assert(r == "00000007"); + r = std.string.format("%.8x", 10); + assert(r == "0000000a"); + + r = std.string.format("%-3d", 7); + assert(r == "7 "); + + r = std.string.format("%*d", -3, 7); + assert(r == "7 "); + + r = std.string.format("%.*d", -3, 7); + assert(r == "7"); + + typedef int myint; + myint m = -7; + r = std.string.format(m); + assert(r == "-7"); + + r = std.string.format("abc"c); + assert(r == "abc"); + r = std.string.format("def"w); + assert(r == "def"); + r = std.string.format("ghi"d); + assert(r == "ghi"); + + void* p = cast(void*)0xDEADBEEF; + r = std.string.format(p); + assert(r == "DEADBEEF"); + + r = std.string.format("%#x", 0xabcd); + assert(r == "0xabcd"); + r = std.string.format("%#X", 0xABCD); + assert(r == "0XABCD"); + + r = std.string.format("%#o", 012345); + assert(r == "012345"); + r = std.string.format("%o", 9); + assert(r == "11"); + + r = std.string.format("%+d", 123); + assert(r == "+123"); + r = std.string.format("%+d", -123); + assert(r == "-123"); + r = std.string.format("% d", 123); + assert(r == " 123"); + r = std.string.format("% d", -123); + assert(r == "-123"); + + r = std.string.format("%%"); + assert(r == "%"); + + r = std.string.format("%d", true); + assert(r == "1"); + r = std.string.format("%d", false); + assert(r == "0"); + + r = std.string.format("%d", 'a'); + assert(r == "97"); + wchar wc = 'a'; + r = std.string.format("%d", wc); + assert(r == "97"); + dchar dc = 'a'; + r = std.string.format("%d", dc); + assert(r == "97"); + + byte b = byte.max; + r = std.string.format("%x", b); + assert(r == "7f"); + r = std.string.format("%x", ++b); + assert(r == "80"); + r = std.string.format("%x", ++b); + assert(r == "81"); + + short sh = short.max; + r = std.string.format("%x", sh); + assert(r == "7fff"); + r = std.string.format("%x", ++sh); + assert(r == "8000"); + r = std.string.format("%x", ++sh); + assert(r == "8001"); + + i = int.max; + r = std.string.format("%x", i); + assert(r == "7fffffff"); + r = std.string.format("%x", ++i); + assert(r == "80000000"); + r = std.string.format("%x", ++i); + assert(r == "80000001"); + + r = std.string.format("%x", 10); + assert(r == "a"); + r = std.string.format("%X", 10); + assert(r == "A"); + r = std.string.format("%x", 15); + assert(r == "f"); + r = std.string.format("%X", 15); + assert(r == "F"); + + Object c = null; + r = std.string.format(c); + assert(r == "null"); + + enum TestEnum + { + Value1, Value2 + } + r = std.string.format("%s", TestEnum.Value2); + assert(r == "1"); + + char[5][int] aa = ([3:"hello", 4:"betty"]); + r = std.string.format("%s", aa.values); + assert(r == "[[h,e,l,l,o],[b,e,t,t,y]]"); + r = std.string.format("%s", aa); + assert(r == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); + + static const dchar[] ds = ['a','b']; + for (int j = 0; j < ds.length; ++j) + { + r = std.string.format(" %d", ds[j]); + if (j == 0) + assert(r == " 97"); + else + assert(r == " 98"); + } + + r = std.string.format(">%14d<, ", 15, [1,2,3]); + assert(r == "> 15<, [1,2,3]"); +} + diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/std/math.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/std/math.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,1954 @@ +// Written in the D programming language + +/** + * Macros: + * WIKI = Phobos/StdMath + * + * TABLE_SV = + * + * $0
Special Values
+ * SVH = $(TR $(TH $1) $(TH $2)) + * SV = $(TR $(TD $1) $(TD $2)) + * + * NAN = $(RED NAN) + * SUP = $0 + * GAMMA = Γ + * INTEGRAL = ∫ + * INTEGRATE = $(BIG ∫$(SMALL $1)$2) + * POWER = $1$2 + * BIGSUM = $(BIG Σ $2$(SMALL $1)) + * CHOOSE = $(BIG () $(SMALL $1)$(SMALL $2) $(BIG )) + * PLUSMN = ± + * INFIN = ∞ + * PI = π + * LT = < + * GT = > + */ + +/* + * Author: + * Walter Bright + * Copyright: + * Copyright (c) 2001-2005 by Digital Mars, + * All Rights Reserved, + * www.digitalmars.com + * Copyright (c) 2007 by Tomas Lindquist Olsen + * License: + * 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: + * + *
    + *
  • 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. + *
  • + *
  • Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + *
  • + *
  • This notice may not be removed or altered from any source + * distribution. + *
  • + *
+ */ + + +module std.math; + +//debug=math; // uncomment to turn on debugging printf's + +private import std.c.stdio; +private import std.c.math; + +class NotImplemented : Error +{ + this(string msg) + { + super(msg ~ "not implemented"); + } +} + +const real E = 2.7182818284590452354L; /** e */ +const real LOG2T = 0x1.a934f0979a3715fcp+1; /** log210 */ // 3.32193 fldl2t +const real LOG2E = 0x1.71547652b82fe178p+0; /** log2e */ // 1.4427 fldl2e +const real LOG2 = 0x1.34413509f79fef32p-2; /** log102 */ // 0.30103 fldlg2 +const real LOG10E = 0.43429448190325182765; /** log10e */ +const real LN2 = 0x1.62e42fefa39ef358p-1; /** ln 2 */ // 0.693147 fldln2 +const real LN10 = 2.30258509299404568402; /** ln 10 */ +const real PI = 0x1.921fb54442d1846ap+1; /** $(PI) */ // 3.14159 fldpi +const real PI_2 = 1.57079632679489661923; /** $(PI) / 2 */ +const real PI_4 = 0.78539816339744830962; /** $(PI) / 4 */ +const real M_1_PI = 0.31830988618379067154; /** 1 / $(PI) */ +const real M_2_PI = 0.63661977236758134308; /** 2 / $(PI) */ +const real M_2_SQRTPI = 1.12837916709551257390; /** 2 / √$(PI) */ +const real SQRT2 = 1.41421356237309504880; /** √2 */ +const real SQRT1_2 = 0.70710678118654752440; /** √½ */ + +/* + Octal versions: + PI/64800 0.00001 45530 36176 77347 02143 15351 61441 26767 + PI/180 0.01073 72152 11224 72344 25603 54276 63351 22056 + PI/8 0.31103 75524 21026 43021 51423 06305 05600 67016 + SQRT(1/PI) 0.44067 27240 41233 33210 65616 51051 77327 77303 + 2/PI 0.50574 60333 44710 40522 47741 16537 21752 32335 + PI/4 0.62207 73250 42055 06043 23046 14612 13401 56034 + SQRT(2/PI) 0.63041 05147 52066 24106 41762 63612 00272 56161 + + PI 3.11037 55242 10264 30215 14230 63050 56006 70163 + LOG2 0.23210 11520 47674 77674 61076 11263 26013 37111 + */ + + +/*********************************** + * Calculates the absolute value + * + * For complex numbers, abs(z) = sqrt( $(POWER z.re, 2) + $(POWER z.im, 2) ) + * = hypot(z.re, z.im). + */ +real abs(real x) +{ + return fabs(x); +} + +/** ditto */ +long abs(long x) +{ + return x>=0 ? x : -x; +} + +/** ditto */ +int abs(int x) +{ + return x>=0 ? x : -x; +} + +/** ditto */ +real abs(creal z) +{ + return hypot(z.re, z.im); +} + +/** ditto */ +real abs(ireal y) +{ + return fabs(y.im); +} + + +unittest +{ + assert(isPosZero(abs(-0.0L))); + assert(isnan(abs(real.nan))); + assert(abs(-real.infinity) == real.infinity); + assert(abs(-3.2Li) == 3.2L); + assert(abs(71.6Li) == 71.6L); + assert(abs(-56) == 56); + assert(abs(2321312L) == 2321312L); + assert(abs(-1+1i) == sqrt(2.0)); +} + +/*********************************** + * Complex conjugate + * + * conj(x + iy) = x - iy + * + * Note that z * conj(z) = $(POWER z.re, 2) - $(POWER z.im, 2) + * is always a real number + */ +creal conj(creal z) +{ + return z.re - z.im*1i; +} + +/** ditto */ +ireal conj(ireal y) +{ + return -y; +} + +unittest +{ + assert(conj(7 + 3i) == 7-3i); + ireal z = -3.2Li; + assert(conj(z) == -z); +} + +/*********************************** + * Returns cosine of x. x is in radians. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH cos(x)) $(TH invalid?)) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes) ) + * ) + * Bugs: + * Results are undefined if |x| >= $(POWER 2,64). + */ + +pragma(LLVM_internal, "intrinsic", "llvm.cos.f32") +float cos(float x); + +pragma(LLVM_internal, "intrinsic", "llvm.cos.f64") { +double cos(double x); // ditto +real cos(real x); /// ditto +} + + +/*********************************** + * Returns sine of x. x is in radians. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH sin(x)) $(TH invalid?)) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes)) + * ) + * Bugs: + * Results are undefined if |x| >= $(POWER 2,64). + */ + +pragma(LLVM_internal, "intrinsic", "llvm.sin.f32") +float sin(float x); + +pragma(LLVM_internal, "intrinsic", "llvm.sin.f64") { +double sin(double x); // ditto +real sin(real x); /// ditto +} + + +/**************************************************************************** + * Returns tangent of x. x is in radians. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH tan(x)) $(TH invalid?)) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes)) + * ) + */ + +version(D_InlineAsm_X86) +real tan(real x) +{ + + asm + { + fld x[EBP] ; // load theta + fxam ; // test for oddball values + fstsw AX ; + sahf ; + jc trigerr ; // x is NAN, infinity, or empty + // 387's can handle denormals +SC18: fptan ; + fstp ST(0) ; // dump X, which is always 1 + fstsw AX ; + sahf ; + jnp Lret ; // C2 = 1 (x is out of range) + + // Do argument reduction to bring x into range + fldpi ; + fxch ; +SC17: fprem1 ; + fstsw AX ; + sahf ; + jp SC17 ; + fstp ST(1) ; // remove pi from stack + jmp SC18 ; + +trigerr: + jnp Lret ; // if theta is NAN, return theta + fstp ST(0) ; // dump theta + } + return real.nan; + +Lret: + ; +} +else +{ +real tan(real x) { return std.c.math.atan(x); } +} + + +unittest +{ + static real vals[][2] = // angle,tan + [ + [ 0, 0], + [ .5, .5463024898], + [ 1, 1.557407725], + [ 1.5, 14.10141995], + [ 2, -2.185039863], + [ 2.5,-.7470222972], + [ 3, -.1425465431], + [ 3.5, .3745856402], + [ 4, 1.157821282], + [ 4.5, 4.637332055], + [ 5, -3.380515006], + [ 5.5,-.9955840522], + [ 6, -.2910061914], + [ 6.5, .2202772003], + [ 10, .6483608275], + + // special angles + [ PI_4, 1], + //[ PI_2, real.infinity], + [ 3*PI_4, -1], + [ PI, 0], + [ 5*PI_4, 1], + //[ 3*PI_2, -real.infinity], + [ 7*PI_4, -1], + [ 2*PI, 0], + + // overflow + [ real.infinity, real.nan], + [ real.nan, real.nan], + //[ 1e+100, real.nan], + ]; + int i; + + for (i = 0; i < vals.length; i++) + { + real x = vals[i][0]; + real r = vals[i][1]; + real t = tan(x); + + //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r); + assert(mfeq(r, t, .0000001)); + + x = -x; + r = -r; + t = tan(x); + //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r); + assert(mfeq(r, t, .0000001)); + } +} + +/*************** + * Calculates the arc cosine of x, + * returning a value ranging from -$(PI)/2 to $(PI)/2. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH acos(x)) $(TH invalid?)) + * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) + * ) + */ +real acos(real x) { return std.c.math.acos(x); } + +/*************** + * Calculates the arc sine of x, + * returning a value ranging from -$(PI)/2 to $(PI)/2. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH asin(x)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) + * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes)) + * ) + */ +real asin(real x) { return std.c.math.asin(x); } + +/*************** + * Calculates the arc tangent of x, + * returning a value ranging from -$(PI)/2 to $(PI)/2. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH atan(x)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes)) + * ) + */ +real atan(real x) { return std.c.math.atan(x); } + +/*************** + * Calculates the arc tangent of y / x, + * returning a value ranging from -$(PI)/2 to $(PI)/2. + * + * $(TABLE_SV + * $(TR $(TH y) $(TH x) $(TH atan(y, x))) + * $(TR $(TD $(NAN)) $(TD anything) $(TD $(NAN)) ) + * $(TR $(TD anything) $(TD $(NAN)) $(TD $(NAN)) ) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) ) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) ) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT)0.0) $(TD $(PLUSMN)$(PI))) + * $(TR $(TD $(PLUSMN)0.0) $(TD -0.0) $(TD $(PLUSMN)$(PI))) + * $(TR $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2) ) + * $(TR $(TD $(LT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2)) + * $(TR $(TD $(GT)0.0) $(TD $(INFIN)) $(TD $(PLUSMN)0.0) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(PLUSMN)$(PI)/2)) + * $(TR $(TD $(GT)0.0) $(TD -$(INFIN)) $(TD $(PLUSMN)$(PI)) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(INFIN)) $(TD $(PLUSMN)$(PI)/4)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4)) + * ) + */ +real atan2(real y, real x) { return std.c.math.atan2(y,x); } + +/*********************************** + * Calculates the hyperbolic cosine of x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH cosh(x)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) ) + * ) + */ +real cosh(real x) { return std.c.math.cosh(x); } + +/*********************************** + * Calculates the hyperbolic sine of x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH sinh(x)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no)) + * ) + */ +real sinh(real x) { return std.c.math.sinh(x); } + +/*********************************** + * Calculates the hyperbolic tangent of x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH tanh(x)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no)) + * ) + */ +real tanh(real x) { return std.c.math.tanh(x); } + +//real acosh(real x) { return std.c.math.acoshl(x); } +//real asinh(real x) { return std.c.math.asinhl(x); } +//real atanh(real x) { return std.c.math.atanhl(x); } + +/*********************************** + * Calculates the inverse hyperbolic cosine of x. + * + * Mathematically, acosh(x) = log(x + sqrt( x*x - 1)) + * + * $(TABLE_DOMRG + * $(DOMAIN 1..$(INFIN)) + * $(RANGE 1..log(real.max), $(INFIN)) ) + * $(TABLE_SV + * $(SVH x, acosh(x) ) + * $(SV $(NAN), $(NAN) ) + * $(SV <1, $(NAN) ) + * $(SV 1, 0 ) + * $(SV +$(INFIN),+$(INFIN)) + * ) + */ +real acosh(real x) +{ + if (x > 1/real.epsilon) + return LN2 + log(x); + else + return log(x + sqrt(x*x - 1)); +} + +unittest +{ + assert(isnan(acosh(0.9))); + assert(isnan(acosh(real.nan))); + assert(acosh(1)==0.0); + assert(acosh(real.infinity) == real.infinity); +} + +/*********************************** + * Calculates the inverse hyperbolic sine of x. + * + * Mathematically, + * --------------- + * asinh(x) = log( x + sqrt( x*x + 1 )) // if x >= +0 + * asinh(x) = -log(-x + sqrt( x*x + 1 )) // if x <= -0 + * ------------- + * + * $(TABLE_SV + * $(SVH x, asinh(x) ) + * $(SV $(NAN), $(NAN) ) + * $(SV $(PLUSMN)0, $(PLUSMN)0 ) + * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN)) + * ) + */ +real asinh(real x) +{ + if (fabs(x) > 1 / real.epsilon) // beyond this point, x*x + 1 == x*x + return copysign(LN2 + log(fabs(x)), x); + else + { + // sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) ) + return copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x); + } +} + +unittest +{ + assert(isPosZero(asinh(0.0))); + assert(isNegZero(asinh(-0.0))); + assert(asinh(real.infinity) == real.infinity); + assert(asinh(-real.infinity) == -real.infinity); + assert(isnan(asinh(real.nan))); +} + +/*********************************** + * Calculates the inverse hyperbolic tangent of x, + * returning a value from ranging from -1 to 1. + * + * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2 + * + * + * $(TABLE_DOMRG + * $(DOMAIN -$(INFIN)..$(INFIN)) + * $(RANGE -1..1) ) + * $(TABLE_SV + * $(SVH x, acosh(x) ) + * $(SV $(NAN), $(NAN) ) + * $(SV $(PLUSMN)0, $(PLUSMN)0) + * $(SV -$(INFIN), -0) + * ) + */ +real atanh(real x) +{ + // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) ) + return 0.5 * log1p( 2 * x / (1 - x) ); +} + +unittest +{ + assert(isPosZero(atanh(0.0))); + assert(isNegZero(atanh(-0.0))); + assert(isnan(atanh(real.nan))); + assert(isnan(atanh(-real.infinity))); +} + +/***************************************** + * Returns x rounded to a long value using the current rounding mode. + * If the integer value of x is + * greater than long.max, the result is + * indeterminate. + */ +long rndtol(real x); /* intrinsic */ + + +/***************************************** + * Returns x rounded to a long value using the FE_TONEAREST rounding mode. + * If the integer value of x is + * greater than long.max, the result is + * indeterminate. + */ +extern (C) real rndtonl(real x); + +/*************************************** + * Compute square root of x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH sqrt(x)) $(TH invalid?)) + * $(TR $(TD -0.0) $(TD -0.0) $(TD no)) + * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no)) + * ) + */ + +pragma(LLVM_internal, "intrinsic", "llvm.sqrt.f32") +float sqrt(float x); /* intrinsic */ + +pragma(LLVM_internal, "intrinsic", "llvm.sqrt.f64") { +double sqrt(double x); /* intrinsic */ /// ditto +real sqrt(real x); /* intrinsic */ /// ditto +} + +creal sqrt(creal z) +{ + creal c; + real x,y,w,r; + + if (z == 0) + { + c = 0 + 0i; + } + else + { real z_re = z.re; + real z_im = z.im; + + x = fabs(z_re); + y = fabs(z_im); + if (x >= y) + { + r = y / x; + w = sqrt(x) * sqrt(0.5 * (1 + sqrt(1 + r * r))); + } + else + { + r = x / y; + w = sqrt(y) * sqrt(0.5 * (r + sqrt(1 + r * r))); + } + + if (z_re >= 0) + { + c = w + (z_im / (w + w)) * 1.0i; + } + else + { + if (z_im < 0) + w = -w; + c = z_im / (w + w) + w * 1.0i; + } + } + return c; +} + +/********************** + * Calculates e$(SUP x). + * + * $(TABLE_SV + * $(TR $(TH x) $(TH exp(x))) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) ) + * $(TR $(TD -$(INFIN)) $(TD +0.0) ) + * ) + */ +real exp(real x) { return std.c.math.exp(x); } + +/********************** + * Calculates 2$(SUP x). + * + * $(TABLE_SV + * $(TR $(TH x) $(TH exp2(x))) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN))) + * $(TR $(TD -$(INFIN)) $(TD +0.0)) + * ) + */ +real exp2(real x) { return std.c.math.exp2(x); } + +/****************************************** + * Calculates the value of the natural logarithm base (e) + * raised to the power of x, minus 1. + * + * For very small x, expm1(x) is more accurate + * than exp(x)-1. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH e$(SUP x)-1)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0)) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN))) + * $(TR $(TD -$(INFIN)) $(TD -1.0)) + * ) + */ + +real expm1(real x) { return std.c.math.expm1(x); } + + +/********************************************************************* + * Separate floating point value into significand and exponent. + * + * Returns: + * Calculate and return x and exp such that + * value =x*2$(SUP exp) and + * .5 $(LT)= |x| $(LT) 1.0
+ * x has same sign as value. + * + * $(TABLE_SV + * $(TR $(TH value) $(TH returns) $(TH exp)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD 0)) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD int.max)) + * $(TR $(TD -$(INFIN)) $(TD -$(INFIN)) $(TD int.min)) + * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD int.min)) + * ) + */ + + +real frexp(real value, out int exp) +{ + ushort* vu = cast(ushort*)&value; + long* vl = cast(long*)&value; + uint ex; + + // If exponent is non-zero + ex = vu[4] & 0x7FFF; + if (ex) + { + if (ex == 0x7FFF) + { // infinity or NaN + if (*vl & 0x7FFFFFFFFFFFFFFF) // if NaN + { *vl |= 0xC000000000000000; // convert $(NAN)S to $(NAN)Q + exp = int.min; + } + else if (vu[4] & 0x8000) + { // negative infinity + exp = int.min; + } + else + { // positive infinity + exp = int.max; + } + } + else + { + exp = ex - 0x3FFE; + vu[4] = cast(ushort)((0x8000 & vu[4]) | 0x3FFE); + } + } + else if (!*vl) + { + // value is +-0.0 + exp = 0; + } + else + { // denormal + int i = -0x3FFD; + + do + { + i--; + *vl <<= 1; + } while (*vl > 0); + exp = i; + vu[4] = cast(ushort)((0x8000 & vu[4]) | 0x3FFE); + } + return value; +} + + +unittest +{ + static real vals[][3] = // x,frexp,exp + [ + [0.0, 0.0, 0], + [-0.0, -0.0, 0], + [1.0, .5, 1], + [-1.0, -.5, 1], + [2.0, .5, 2], + [155.67e20, 0x1.A5F1C2EB3FE4Fp-1, 74], // normal + [1.0e-320, 0.98829225, -1063], + [real.min, .5, -16381], + [real.min/2.0L, .5, -16382], // denormal + + [real.infinity,real.infinity,int.max], + [-real.infinity,-real.infinity,int.min], + [real.nan,real.nan,int.min], + [-real.nan,-real.nan,int.min], + + // Don't really support signalling nan's in D + //[real.nans,real.nan,int.min], + //[-real.nans,-real.nan,int.min], + ]; + int i; + + for (i = 0; i < vals.length; i++) + { + real x = vals[i][0]; + real e = vals[i][1]; + int exp = cast(int)vals[i][2]; + int eptr; + real v = frexp(x, eptr); + + //printf("frexp(%Lg) = %.8Lg, should be %.8Lg, eptr = %d, should be %d\n", x, v, e, eptr, exp); + assert(mfeq(e, v, .0000001)); + assert(exp == eptr); + } +} + + +/****************************************** + * Extracts the exponent of x as a signed integral value. + * + * If x is not a special value, the result is the same as + * cast(int)logb(x). + * + * $(TABLE_SV + * $(TR $(TH x) $(TH ilogb(x)) $(TH Range error?)) + * $(TR $(TD 0) $(TD FP_ILOGB0) $(TD yes)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD int.max) $(TD no)) + * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no)) + * ) + */ +int ilogb(real x) { return std.c.math.ilogb(x); } + +alias std.c.math.FP_ILOGB0 FP_ILOGB0; +alias std.c.math.FP_ILOGBNAN FP_ILOGBNAN; + + +/******************************************* + * Compute n * 2$(SUP exp) + * References: frexp + */ + +real ldexp(real n, int exp) { return std.c.math.ldexp(n, exp); } + +/************************************** + * Calculate the natural logarithm of x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH log(x)) $(TH divide by 0?) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no)) + * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes)) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no)) + * ) + */ + +real log(real x) { return std.c.math.log(x); } + +/************************************** + * Calculate the base-10 logarithm of x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH log10(x)) $(TH divide by 0?) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no)) + * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes)) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no)) + * ) + */ + +real log10(real x) { return std.c.math.log10(x); } + +/****************************************** + * Calculates the natural logarithm of 1 + x. + * + * For very small x, log1p(x) will be more accurate than + * log(1 + x). + * + * $(TABLE_SV + * $(TR $(TH x) $(TH log1p(x)) $(TH divide by 0?) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) $(TD no)) + * $(TR $(TD -1.0) $(TD -$(INFIN)) $(TD yes) $(TD no)) + * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD no) $(TD yes)) + * $(TR $(TD +$(INFIN)) $(TD -$(INFIN)) $(TD no) $(TD no)) + * ) + */ + +real log1p(real x) { return std.c.math.log1p(x); } + +/*************************************** + * Calculates the base-2 logarithm of x: + * log2x + * + * $(TABLE_SV + * $(TR $(TH x) $(TH log2(x)) $(TH divide by 0?) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no) ) + * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes) ) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) ) + * ) + */ +real log2(real x) { return std.c.math.log2(x); } + +/***************************************** + * Extracts the exponent of x as a signed integral value. + * + * If x is subnormal, it is treated as if it were normalized. + * For a positive, finite x: + * + * ----- + * 1 <= $(I x) * FLT_RADIX$(SUP -logb(x)) < FLT_RADIX + * ----- + * + * $(TABLE_SV + * $(TR $(TH x) $(TH logb(x)) $(TH divide by 0?) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) $(TD no)) + * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) ) + * ) + */ +real logb(real x) { return std.c.math.logb(x); } + +/************************************ + * Calculates the remainder from the calculation x/y. + * Returns: + * The value of x - i * y, where i is the number of times that y can + * be completely subtracted from x. The result has the same sign as x. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH y) $(TH modf(x, y)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD no)t 0.0 $(TD $(PLUSMN)0.0) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD !=$(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD x) $(TD no)) + * ) + */ +real modf(real x, inout real y) +{ +double Y = y; +auto tmp = std.c.math.modf(x,&Y); +y = Y; +return tmp; +} + +/************************************* + * Efficiently calculates x * 2$(SUP n). + * + * scalbn handles underflow and overflow in + * the same fashion as the basic arithmetic operators. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH scalb(x))) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) ) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) ) + * ) + */ +real scalbn(real x, int n) +{ + version (linux) + return std.c.math.scalbn(x, n); + else + throw new NotImplemented("scalbn"); +} + +/*************** + * Calculates the cube root x. + * + * $(TABLE_SV + * $(TR $(TH $(I x)) $(TH cbrt(x)) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) ) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no) ) + * ) + */ +real cbrt(real x) { return std.c.math.cbrt(x); } + + +/******************************* + * Returns |x| + * + * $(TABLE_SV + * $(TR $(TH x) $(TH fabs(x))) + * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) ) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) ) + * ) + */ +real fabs(real x) { return std.c.math.fabs(x); } + + +/*********************************************************************** + * Calculates the length of the + * hypotenuse of a right-angled triangle with sides of length x and y. + * The hypotenuse is the value of the square root of + * the sums of the squares of x and y: + * + * sqrt(x² + y²) + * + * Note that hypot(x, y), hypot(y, x) and + * hypot(x, -y) are equivalent. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH y) $(TH hypot(x, y)) $(TH invalid?)) + * $(TR $(TD x) $(TD $(PLUSMN)0.0) $(TD |x|) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD y) $(TD +$(INFIN)) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD +$(INFIN)) $(TD no)) + * ) + */ + +real hypot(real x, real y) +{ + /* + * This is based on code from: + * Cephes Math Library Release 2.1: January, 1989 + * Copyright 1984, 1987, 1989 by Stephen L. Moshier + * Direct inquiries to 30 Frost Street, Cambridge, MA 02140 + */ + + const int PRECL = 32; + const int MAXEXPL = real.max_exp; //16384; + const int MINEXPL = real.min_exp; //-16384; + + real xx, yy, b, re, im; + int ex, ey, e; + + // Note, hypot(INFINITY, NAN) = INFINITY. + if (isinf(x) || isinf(y)) + return real.infinity; + + if (isnan(x)) + return x; + if (isnan(y)) + return y; + + re = fabs(x); + im = fabs(y); + + if (re == 0.0) + return im; + if (im == 0.0) + return re; + + // Get the exponents of the numbers + xx = frexp(re, ex); + yy = frexp(im, ey); + + // Check if one number is tiny compared to the other + e = ex - ey; + if (e > PRECL) + return re; + if (e < -PRECL) + return im; + + // Find approximate exponent e of the geometric mean. + e = (ex + ey) >> 1; + + // Rescale so mean is about 1 + xx = ldexp(re, -e); + yy = ldexp(im, -e); + + // Hypotenuse of the right triangle + b = sqrt(xx * xx + yy * yy); + + // Compute the exponent of the answer. + yy = frexp(b, ey); + ey = e + ey; + + // Check it for overflow and underflow. + if (ey > MAXEXPL + 2) + { + //return __matherr(_OVERFLOW, INFINITY, x, y, "hypotl"); + return real.infinity; + } + if (ey < MINEXPL - 2) + return 0.0; + + // Undo the scaling + b = ldexp(b, e); + return b; +} + +unittest +{ + static real vals[][3] = // x,y,hypot + [ + [ 0, 0, 0], + [ 0, -0, 0], + [ 3, 4, 5], + [ -300, -400, 500], + [ real.min, real.min, 4.75473e-4932L], + [ real.max/2, real.max/2, 0x1.6a09e667f3bcc908p+16383L /*8.41267e+4931L*/], + [ real.infinity, real.nan, real.infinity], + [ real.nan, real.nan, real.nan], + ]; + int i; + + for (i = 0; i < vals.length; i++) + { + real x = vals[i][0]; + real y = vals[i][1]; + real z = vals[i][2]; + real h = hypot(x, y); + + //printf("hypot(%Lg, %Lg) = %Lg, should be %Lg\n", x, y, h, z); + //if (!mfeq(z, h, .0000001)) + //printf("%La\n", h); + assert(mfeq(z, h, .0000001)); + } +} + +/********************************** + * Returns the error function of x. + * + * error function + */ +real erf(real x) { return std.c.math.erf(x); } + +/********************************** + * Returns the complementary error function of x, which is 1 - erf(x). + * + * complementary error function + */ +real erfc(real x) { return std.c.math.erfc(x); } + +/*********************************** + * Natural logarithm of gamma function. + * + * Returns the base e (2.718...) logarithm of the absolute + * value of the gamma function of the argument. + * + * For reals, lgamma is equivalent to log(fabs(gamma(x))). + * + * $(TABLE_SV + * $(TR $(TH x) $(TH lgamma(x)) $(TH invalid?)) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD integer <= 0) $(TD +$(INFIN)) $(TD yes)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) $(TD no)) + * ) + */ +/* Documentation prepared by Don Clugston */ +real lgamma(real x) +{ + return std.c.math.lgamma(x); + + // Use etc.gamma.lgamma for those C systems that are missing it +} + +/*********************************** + * The Gamma function, $(GAMMA)(x) + * + * $(GAMMA)(x) is a generalisation of the factorial function + * to real and complex numbers. + * Like x!, $(GAMMA)(x+1) = x*$(GAMMA)(x). + * + * Mathematically, if z.re > 0 then + * $(GAMMA)(z) =$(INTEGRAL)0$(INFIN)tz-1e-tdt + * + * $(TABLE_SV + * $(TR $(TH x) $(TH $(GAMMA)(x)) $(TH invalid?)) + * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)$(INFIN)) $(TD yes)) + * $(TR $(TD integer $(GT)0) $(TD (x-1)!) $(TD no)) + * $(TR $(TD integer $(LT)0) $(TD $(NAN)) $(TD yes)) + * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no)) + * $(TR $(TD -$(INFIN)) $(TD $(NAN)) $(TD yes)) + * ) + * + * References: + * $(LINK http://en.wikipedia.org/wiki/Gamma_function), + * $(LINK http://www.netlib.org/cephes/ldoubdoc.html#gamma) + */ +/* Documentation prepared by Don Clugston */ +real tgamma(real x) +{ + return std.c.math.tgamma(x); + + // Use etc.gamma.tgamma for those C systems that are missing it +} + +/************************************** + * Returns the value of x rounded upward to the next integer + * (toward positive infinity). + */ +real ceil(real x) { return std.c.math.ceil(x); } + +/************************************** + * Returns the value of x rounded downward to the next integer + * (toward negative infinity). + */ +real floor(real x) { return std.c.math.floor(x); } + +/****************************************** + * Rounds x to the nearest integer value, using the current rounding + * mode. + * + * Unlike the rint functions, nearbyint does not raise the + * FE_INEXACT exception. + */ +real nearbyint(real x) { return std.c.math.nearbyint(x); } + +/********************************** + * Rounds x to the nearest integer value, using the current rounding + * mode. + * If the return value is not equal to x, the FE_INEXACT + * exception is raised. + * nearbyint performs + * the same operation, but does not set the FE_INEXACT exception. + */ +real rint(real x) { return std.c.math.rint(x); } + +/*************************************** + * Rounds x to the nearest integer value, using the current rounding + * mode. + */ +long lrint(real x) +{ + version (linux) + return std.c.math.llrint(x); + else + throw new NotImplemented("lrint"); +} + +/******************************************* + * Return the value of x rounded to the nearest integer. + * If the fractional part of x is exactly 0.5, the return value is rounded to + * the even integer. + */ +real round(real x) { return std.c.math.round(x); } + +/********************************************** + * Return the value of x rounded to the nearest integer. + * + * If the fractional part of x is exactly 0.5, the return value is rounded + * away from zero. + */ +long lround(real x) +{ + version (linux) + return std.c.math.llround(x); + else + throw new NotImplemented("lround"); +} + +/**************************************************** + * Returns the integer portion of x, dropping the fractional portion. + * + * This is also known as "chop" rounding. + */ +real trunc(real x) { return std.c.math.trunc(x); } + +/**************************************************** + * Calculate the remainder x REM y, following IEC 60559. + * + * REM is the value of x - y * n, where n is the integer nearest the exact + * value of x / y. + * If |n - x / y| == 0.5, n is even. + * If the result is zero, it has the same sign as x. + * Otherwise, the sign of the result is the sign of x / y. + * Precision mode has no effect on the remainder functions. + * + * remquo returns n in the parameter n. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH y) $(TH remainder(x, y)) $(TH n) $(TH invalid?)) + * $(TR $(TD $(PLUSMN)0.0) $(TD no)t 0.0 $(TD $(PLUSMN)0.0) $(TD 0.0) $(TD no)) + * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(NAN)) $(TD ?) $(TD yes)) + * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD ?) $(TD yes)) + * $(TR $(TD != $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD x) $(TD ?) $(TD no)) + * ) + */ +real remainder(real x, real y) { return std.c.math.remainder(x, y); } + +real remquo(real x, real y, out int n) /// ditto +{ + version (linux) + return std.c.math.remquo(x, y, &n); + else + throw new NotImplemented("remquo"); +} + +/********************************* + * Returns !=0 if e is a NaN. + */ + +int isnan(real e) +{ + ushort* pe = cast(ushort *)&e; + ulong* ps = cast(ulong *)&e; + + return (pe[4] & 0x7FFF) == 0x7FFF && + *ps & 0x7FFFFFFFFFFFFFFF; +} + +unittest +{ + assert(isnan(float.nan)); + assert(isnan(-double.nan)); + assert(isnan(real.nan)); + + assert(!isnan(53.6)); + assert(!isnan(float.infinity)); +} + +/********************************* + * Returns !=0 if e is finite. + */ + +int isfinite(real e) +{ + ushort* pe = cast(ushort *)&e; + + return (pe[4] & 0x7FFF) != 0x7FFF; +} + +unittest +{ + assert(isfinite(1.23)); + assert(!isfinite(double.infinity)); + assert(!isfinite(float.nan)); +} + + +/********************************* + * Returns !=0 if x is normalized. + */ + +/* Need one for each format because subnormal floats might + * be converted to normal reals. + */ + +int isnormal(float x) +{ + uint *p = cast(uint *)&x; + uint e; + + e = *p & 0x7F800000; + //printf("e = x%x, *p = x%x\n", e, *p); + return e && e != 0x7F800000; +} + +/// ditto + +int isnormal(double d) +{ + uint *p = cast(uint *)&d; + uint e; + + e = p[1] & 0x7FF00000; + return e && e != 0x7FF00000; +} + +/// ditto + +int isnormal(real e) +{ + ushort* pe = cast(ushort *)&e; + long* ps = cast(long *)&e; + + return (pe[4] & 0x7FFF) != 0x7FFF && *ps < 0; +} + +unittest +{ + float f = 3; + double d = 500; + real e = 10e+48; + + assert(isnormal(f)); + assert(isnormal(d)); + assert(isnormal(e)); +} + +/********************************* + * Is number subnormal? (Also called "denormal".) + * Subnormals have a 0 exponent and a 0 most significant mantissa bit. + */ + +/* Need one for each format because subnormal floats might + * be converted to normal reals. + */ + +int issubnormal(float f) +{ + uint *p = cast(uint *)&f; + + //printf("*p = x%x\n", *p); + return (*p & 0x7F800000) == 0 && *p & 0x007FFFFF; +} + +unittest +{ + float f = 3.0; + + for (f = 1.0; !issubnormal(f); f /= 2) + assert(f != 0); +} + +/// ditto + +int issubnormal(double d) +{ + uint *p = cast(uint *)&d; + + return (p[1] & 0x7FF00000) == 0 && (p[0] || p[1] & 0x000FFFFF); +} + +unittest +{ + double f; + + for (f = 1; !issubnormal(f); f /= 2) + assert(f != 0); +} + +/// ditto + +int issubnormal(real e) +{ + ushort* pe = cast(ushort *)&e; + long* ps = cast(long *)&e; + + return (pe[4] & 0x7FFF) == 0 && *ps > 0; +} + +unittest +{ + real f; + + for (f = 1; !issubnormal(f); f /= 2) + assert(f != 0); +} + +/********************************* + * Return !=0 if e is $(PLUSMN)$(INFIN). + */ + +int isinf(real e) +{ + ushort* pe = cast(ushort *)&e; + ulong* ps = cast(ulong *)&e; + + return (pe[4] & 0x7FFF) == 0x7FFF && + *ps == 0x8000000000000000; +} + +unittest +{ + assert(isinf(float.infinity)); + assert(!isinf(float.nan)); + assert(isinf(double.infinity)); + assert(isinf(-real.infinity)); + + assert(isinf(-1.0 / 0.0)); +} + +/********************************* + * Return 1 if sign bit of e is set, 0 if not. + */ + +int signbit(real e) +{ + ubyte* pe = cast(ubyte *)&e; + +//printf("e = %Lg\n", e); + return (pe[9] & 0x80) != 0; +} + +unittest +{ + debug (math) printf("math.signbit.unittest\n"); + assert(!signbit(float.nan)); + assert(signbit(-float.nan)); + assert(!signbit(168.1234)); + assert(signbit(-168.1234)); + assert(!signbit(0.0)); + assert(signbit(-0.0)); +} + +/********************************* + * Return a value composed of to with from's sign bit. + */ + +real copysign(real to, real from) +{ + ubyte* pto = cast(ubyte *)&to; + ubyte* pfrom = cast(ubyte *)&from; + + pto[9] &= 0x7F; + pto[9] |= pfrom[9] & 0x80; + + return to; +} + +unittest +{ + real e; + + e = copysign(21, 23.8); + assert(e == 21); + + e = copysign(-21, 23.8); + assert(e == 21); + + e = copysign(21, -23.8); + assert(e == -21); + + e = copysign(-21, -23.8); + assert(e == -21); + + e = copysign(real.nan, -23.8); + assert(isnan(e) && signbit(e)); +} + +/****************************************** + * Creates a quiet NAN with the information from tagp[] embedded in it. + */ +real nan(char[] tagp) { return std.c.math.nan((tagp~\0).ptr); } + +/****************************************** + * Calculates the next representable value after x in the direction of y. + * + * If y $(GT) x, the result will be the next largest floating-point value; + * if y $(LT) x, the result will be the next smallest value. + * If x == y, the result is y. + * The FE_INEXACT and FE_OVERFLOW exceptions will be raised if x is finite and + * the function result is infinite. The FE_INEXACT and FE_UNDERFLOW + * exceptions will be raised if the function value is subnormal, and x is + * not equal to y. + */ +real nextafter(real x, real y) +{ + version (linux) + return std.c.math.nextafterl(x, y); + else + throw new NotImplemented("nextafter"); +} + +//real nexttoward(real x, real y) { return std.c.math.nexttowardl(x, y); } + +/******************************************* + * Returns the positive difference between x and y. + * Returns: + * $(TABLE_SV + * $(TR $(TH x, y) $(TH fdim(x, y))) + * $(TR $(TD x $(GT) y) $(TD x - y)) + * $(TR $(TD x $(LT)= y) $(TD +0.0)) + * ) + */ +real fdim(real x, real y) { return (x > y) ? x - y : +0.0; } + +/**************************************** + * Returns the larger of x and y. + */ +real fmax(real x, real y) { return x > y ? x : y; } + +/**************************************** + * Returns the smaller of x and y. + */ +real fmin(real x, real y) { return x < y ? x : y; } + +/************************************** + * Returns (x * y) + z, rounding only once according to the + * current rounding mode. + */ +real fma(real x, real y, real z) { return (x * y) + z; } + +/******************************************************************* + * Fast integral powers. + */ + +pragma(LLVM_internal, "intrinsic", "llvm.powi.f32") +{ +float pow(float x, uint n); +/// ditto +float pow(float x, int n); +} + +pragma(LLVM_internal, "intrinsic", "llvm.powi.f64") +{ +/// ditto +double pow(double x, uint n); +/// ditto +double pow(double x, int n); +/// ditto +real pow(real x, uint n); +/// ditto +real pow(real x, int n); +} + +/+ +real pow(real x, uint n); +{ + real p; + + switch (n) + { + case 0: + p = 1.0; + break; + + case 1: + p = x; + break; + + case 2: + p = x * x; + break; + + default: + p = 1.0; + while (1) + { + if (n & 1) + p *= x; + n >>= 1; + if (!n) + break; + x *= x; + } + break; + } + return p; +} + +/// ditto +real pow(real x, int n); +{ + if (n < 0) + return pow(x, cast(real)n); + else + return pow(x, cast(uint)n); +} ++/ + +/********************************************* + * Calculates x$(SUP y). + * + * $(TABLE_SV + * $(TR + * $(TH x) $(TH y) $(TH pow(x, y)) $(TH div 0) $(TH invalid?)) + * $(TR + * $(TD anything) $(TD $(PLUSMN)0.0) $(TD 1.0) $(TD no) $(TD no) ) + * $(TR + * $(TD |x| $(GT) 1) $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) ) + * $(TR + * $(TD |x| $(LT) 1) $(TD +$(INFIN)) $(TD +0.0) $(TD no) $(TD no) ) + * $(TR + * $(TD |x| $(GT) 1) $(TD -$(INFIN)) $(TD +0.0) $(TD no) $(TD no) ) + * $(TR + * $(TD |x| $(LT) 1) $(TD -$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) ) + * $(TR + * $(TD +$(INFIN)) $(TD $(GT) 0.0) $(TD +$(INFIN)) $(TD no) $(TD no) ) + * $(TR + * $(TD +$(INFIN)) $(TD $(LT) 0.0) $(TD +0.0) $(TD no) $(TD no) ) + * $(TR + * $(TD -$(INFIN)) $(TD odd integer $(GT) 0.0) $(TD -$(INFIN)) $(TD no) $(TD no) ) + * $(TR + * $(TD -$(INFIN)) $(TD $(GT) 0.0, not odd integer) $(TD +$(INFIN)) $(TD no) $(TD no)) + * $(TR + * $(TD -$(INFIN)) $(TD odd integer $(LT) 0.0) $(TD -0.0) $(TD no) $(TD no) ) + * $(TR + * $(TD -$(INFIN)) $(TD $(LT) 0.0, not odd integer) $(TD +0.0) $(TD no) $(TD no) ) + * $(TR + * $(TD $(PLUSMN)1.0) $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD no) $(TD yes) ) + * $(TR + * $(TD $(LT) 0.0) $(TD finite, nonintegral) $(TD $(NAN)) $(TD no) $(TD yes)) + * $(TR + * $(TD $(PLUSMN)0.0) $(TD odd integer $(LT) 0.0) $(TD $(PLUSMN)$(INFIN)) $(TD yes) $(TD no) ) + * $(TR + * $(TD $(PLUSMN)0.0) $(TD $(LT) 0.0, not odd integer) $(TD +$(INFIN)) $(TD yes) $(TD no)) + * $(TR + * $(TD $(PLUSMN)0.0) $(TD odd integer $(GT) 0.0) $(TD $(PLUSMN)0.0) $(TD no) $(TD no) ) + * $(TR + * $(TD $(PLUSMN)0.0) $(TD $(GT) 0.0, not odd integer) $(TD +0.0) $(TD no) $(TD no) ) + * ) + */ + +pragma(LLVM_internal, "intrinsic", "llvm.pow.f32") +float pow(float x, float y); + +pragma(LLVM_internal, "intrinsic", "llvm.pow.f64") +{ +/// ditto +double pow(double x, double y); +/// ditto +real pow(real x, real y); +} + +/+ +real pow(real x, real y); +{ + version (linux) // C pow() often does not handle special values correctly + { + if (isnan(y)) + return y; + + if (y == 0) + return 1; // even if x is $(NAN) + if (isnan(x) && y != 0) + return x; + if (isinf(y)) + { + if (fabs(x) > 1) + { + if (signbit(y)) + return +0.0; + else + return real.infinity; + } + else if (fabs(x) == 1) + { + return real.nan; + } + else // < 1 + { + if (signbit(y)) + return real.infinity; + else + return +0.0; + } + } + if (isinf(x)) + { + if (signbit(x)) + { long i; + + i = cast(long)y; + if (y > 0) + { + if (i == y && i & 1) + return -real.infinity; + else + return real.infinity; + } + else if (y < 0) + { + if (i == y && i & 1) + return -0.0; + else + return +0.0; + } + } + else + { + if (y > 0) + return real.infinity; + else if (y < 0) + return +0.0; + } + } + + if (x == 0.0) + { + if (signbit(x)) + { long i; + + i = cast(long)y; + if (y > 0) + { + if (i == y && i & 1) + return -0.0; + else + return +0.0; + } + else if (y < 0) + { + if (i == y && i & 1) + return -real.infinity; + else + return real.infinity; + } + } + else + { + if (y > 0) + return +0.0; + else if (y < 0) + return real.infinity; + } + } + } + return std.c.math.powl(x, y); +} ++/ + +unittest +{ + real x = 46; + + assert(pow(x,0) == 1.0); + assert(pow(x,1) == x); + assert(pow(x,2) == x * x); + assert(pow(x,3) == x * x * x); + assert(pow(x,8) == (x * x) * (x * x) * (x * x) * (x * x)); +} + +/**************************************** + * Simple function to compare two floating point values + * to a specified precision. + * Returns: + * 1 match + * 0 nomatch + */ + +private int mfeq(real x, real y, real precision) +{ + if (x == y) + return 1; + if (isnan(x)) + return isnan(y); + if (isnan(y)) + return 0; + return fabs(x - y) <= precision; +} + +// Returns true if x is +0.0 (This function is used in unit tests) +bool isPosZero(real x) +{ + return (x == 0) && (signbit(x) == 0); +} + +// Returns true if x is -0.0 (This function is used in unit tests) +bool isNegZero(real x) +{ + return (x == 0) && signbit(x); +} + +/************************************** + * To what precision is x equal to y? + * + * Returns: the number of mantissa bits which are equal in x and y. + * eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision. + * + * $(TABLE_SV + * $(TR $(TH x) $(TH y) $(TH feqrel(x, y))) + * $(TR $(TD x) $(TD x) $(TD real.mant_dig)) + * $(TR $(TD x) $(TD $(GT)= 2*x) $(TD 0)) + * $(TR $(TD x) $(TD $(LT)= x/2) $(TD 0)) + * $(TR $(TD $(NAN)) $(TD any) $(TD 0)) + * $(TR $(TD any) $(TD $(NAN)) $(TD 0)) + * ) + */ + +int feqrel(real x, real y) +{ + /* Public Domain. Author: Don Clugston, 18 Aug 2005. + */ + + if (x == y) + return real.mant_dig; // ensure diff!=0, cope with INF. + + real diff = fabs(x - y); + + ushort *pa = cast(ushort *)(&x); + ushort *pb = cast(ushort *)(&y); + ushort *pd = cast(ushort *)(&diff); + + // The difference in abs(exponent) between x or y and abs(x-y) + // is equal to the number of mantissa bits of x which are + // equal to y. If negative, x and y have different exponents. + // If positive, x and y are equal to 'bitsdiff' bits. + // AND with 0x7FFF to form the absolute value. + // To avoid out-by-1 errors, we subtract 1 so it rounds down + // if the exponents were different. This means 'bitsdiff' is + // always 1 lower than we want, except that if bitsdiff==0, + // they could have 0 or 1 bits in common. + int bitsdiff = ( ((pa[4]&0x7FFF) + (pb[4]&0x7FFF)-1)>>1) - pd[4]; + + if (pd[4] == 0) + { // Difference is denormal + // For denormals, we need to add the number of zeros that + // lie at the start of diff's mantissa. + // We do this by multiplying by 2^real.mant_dig + diff *= 0x1p+63; + return bitsdiff + real.mant_dig - pd[4]; + } + + if (bitsdiff > 0) + return bitsdiff + 1; // add the 1 we subtracted before + + // Avoid out-by-1 errors when factor is almost 2. + return (bitsdiff == 0) ? (pa[4] == pb[4]) : 0; +} + +unittest +{ + // Exact equality + assert(feqrel(real.max,real.max)==real.mant_dig); + assert(feqrel(0,0)==real.mant_dig); + assert(feqrel(7.1824,7.1824)==real.mant_dig); + assert(feqrel(real.infinity,real.infinity)==real.mant_dig); + + // a few bits away from exact equality + real w=1; + for (int i=1; i0 + a1x + a2x² + a3x³ ... + * + * Uses Horner's rule A(x) = a0 + x(a1 + x(a2 + x(a3 + ...))) + * Params: + * A = array of coefficients a0, a1, etc. + */ +real poly(real x, real[] A) +in +{ + assert(A.length > 0); +} +body +{ + version (D_InlineAsm_X86) + { + version (Windows) + { + asm // assembler by W. Bright + { + // EDX = (A.length - 1) * real.sizeof + mov ECX,A[EBP] ; // ECX = A.length + dec ECX ; + lea EDX,[ECX][ECX*8] ; + add EDX,ECX ; + add EDX,A+4[EBP] ; + fld real ptr [EDX] ; // ST0 = coeff[ECX] + jecxz return_ST ; + fld x[EBP] ; // ST0 = x + fxch ST(1) ; // ST1 = x, ST0 = r + align 4 ; + L2: fmul ST,ST(1) ; // r *= x + fld real ptr -10[EDX] ; + sub EDX,10 ; // deg-- + faddp ST(1),ST ; + dec ECX ; + jne L2 ; + fxch ST(1) ; // ST1 = r, ST0 = x + fstp ST(0) ; // dump x + align 4 ; + return_ST: ; + ; + } + } + else + { + asm // assembler by W. Bright + { + // EDX = (A.length - 1) * real.sizeof + mov ECX,A[EBP] ; // ECX = A.length + dec ECX ; + lea EDX,[ECX*8] ; + lea EDX,[EDX][ECX*4] ; + add EDX,A+4[EBP] ; + fld real ptr [EDX] ; // ST0 = coeff[ECX] + jecxz return_ST ; + fld x[EBP] ; // ST0 = x + fxch ST(1) ; // ST1 = x, ST0 = r + align 4 ; + L2: fmul ST,ST(1) ; // r *= x + fld real ptr -12[EDX] ; + sub EDX,12 ; // deg-- + faddp ST(1),ST ; + dec ECX ; + jne L2 ; + fxch ST(1) ; // ST1 = r, ST0 = x + fstp ST(0) ; // dump x + align 4 ; + return_ST: ; + ; + } + } + } + else + { + int i = A.length - 1; + real r = A[i]; + while (--i >= 0) + { + r *= x; + r += A[i]; + } + return r; + } +} + +unittest +{ + debug (math) printf("math.poly.unittest\n"); + real x = 3.1; + static real pp[] = [56.1, 32.7, 6]; + + assert( poly(x, pp) == (56.1L + (32.7L + 6L * x) * x) ); +} + + diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/std/string.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/std/string.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,4083 @@ + +// Written in the D programming language. + +/** + * String handling functions. + * + * To copy or not to copy? + * When a function takes a string as a parameter, and returns a string, + * is that string the same as the input string, modified in place, or + * is it a modified copy of the input string? The D array convention is + * "copy-on-write". This means that if no modifications are done, the + * original string (or slices of it) can be returned. If any modifications + * are done, the returned string is a copy. + * + * Macros: + * WIKI = Phobos/StdString + * Copyright: + * Public Domain + */ + +/* Author: + * Walter Bright, Digital Mars, www.digitalmars.com + */ + +// The code is not optimized for speed, that will have to wait +// until the design is solidified. + +module std.string; + +//debug=string; // uncomment to turn on debugging printf's + +//private import std.stdio; +private import std.c.stdio; +private import std.c.stdlib; +private import std.c.string; +private import std.utf; +private import std.uni; +private import std.array; +private import std.format; +private import std.ctype; +private import std.stdarg; + +extern (C) +{ + + size_t wcslen(wchar *); + int wcscmp(wchar *, wchar *); +} + +/* ************* Exceptions *************** */ + +/// Thrown on errors in string functions. +class StringException : Exception +{ + this(char[] msg) /// Constructor + { + super(msg); + } +} + +/* ************* Constants *************** */ + +const char[16] hexdigits = "0123456789ABCDEF"; /// 0..9A..F +const char[10] digits = "0123456789"; /// 0..9 +const char[8] octdigits = "01234567"; /// 0..7 +const char[26] lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z +const char[26] uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z +const char[52] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; /// A..Za..z +const char[6] whitespace = " \t\v\r\n\f"; /// ASCII whitespace + +const dchar LS = '\u2028'; /// UTF line separator +const dchar PS = '\u2029'; /// UTF paragraph separator + +/// Newline sequence for this system +version (Windows) + const char[2] newline = "\r\n"; +else version (linux) + const char[1] newline = "\n"; + +/********************************** + * Returns true if c is whitespace + */ + +bool iswhite(dchar c) +{ + return (c <= 0x7F) + ? find(whitespace, c) != -1 + : (c == PS || c == LS); +} + +/********************************* + * Convert string to integer. + */ + +long atoi(char[] s) +{ + return std.c.stdlib.atoi(toStringz(s)); +} + +/************************************* + * Convert string to real. + */ + +real atof(char[] s) +{ char* endptr; + + auto result = strtold(toStringz(s), &endptr); + return result; +} + +/********************************** + * Compare two strings. cmp is case sensitive, icmp is case insensitive. + * Returns: + * + * $(TR $(TD < 0) $(TD s1 < s2)) + * $(TR $(TD = 0) $(TD s1 == s2)) + * $(TR $(TD > 0) $(TD s1 > s2)) + *
+ */ + +int cmp(char[] s1, char[] s2) +{ + auto len = s1.length; + int result; + + //printf("cmp('%.*s', '%.*s')\n", s1, s2); + if (s2.length < len) + len = s2.length; + result = memcmp(s1.ptr, s2.ptr, len); + if (result == 0) + result = cast(int)s1.length - cast(int)s2.length; + return result; +} + +/********************************* + * ditto + */ + +int icmp(char[] s1, char[] s2) +{ + auto len = s1.length; + int result; + + if (s2.length < len) + len = s2.length; + version (Win32) + { + result = memicmp(s1.ptr, s2.ptr, len); + } + version (linux) + { + for (size_t i = 0; i < len; i++) + { + if (s1[i] != s2[i]) + { + char c1 = s1[i]; + char c2 = s2[i]; + + if (c1 >= 'A' && c1 <= 'Z') + c1 += cast(int)'a' - cast(int)'A'; + if (c2 >= 'A' && c2 <= 'Z') + c2 += cast(int)'a' - cast(int)'A'; + result = cast(int)c1 - cast(int)c2; + if (result) + break; + } + } + } + if (result == 0) + result = cast(int)s1.length - cast(int)s2.length; + return result; +} + +unittest +{ + int result; + + debug(string) printf("string.cmp.unittest\n"); + result = icmp("abc", "abc"); + assert(result == 0); + result = icmp(null, null); + assert(result == 0); + result = icmp("", ""); + assert(result == 0); + result = icmp("abc", "abcd"); + assert(result < 0); + result = icmp("abcd", "abc"); + assert(result > 0); + result = icmp("abc", "abd"); + assert(result < 0); + result = icmp("bbc", "abc"); + assert(result > 0); +} + +/* ******************************** + * Converts a D array of chars to a C-style 0 terminated string. + * Deprecated: replaced with toStringz(). + */ + +deprecated char* toCharz(char[] s) +{ + return toStringz(s); +} + +/********************************* + * Convert array of chars s[] to a C-style 0 terminated string. + */ + +char* toStringz(char[] s) + in + { + } + out (result) + { + if (result) + { assert(strlen(result) == s.length); + assert(memcmp(result, s.ptr, s.length) == 0); + } + } + body + { + char[] copy; + + if (s.length == 0) + return ""; + + /+ Unfortunately, this isn't reliable. + We could make this work if string literals are put + in read-only memory and we test if s[] is pointing into + that. + + /* Peek past end of s[], if it's 0, no conversion necessary. + * Note that the compiler will put a 0 past the end of static + * strings, and the storage allocator will put a 0 past the end + * of newly allocated char[]'s. + */ + char* p = &s[0] + s.length; + if (*p == 0) + return s; + +/ + + // Need to make a copy + copy = new char[s.length + 1]; + copy[0..s.length] = s; + copy[s.length] = 0; + return copy.ptr; + } + +unittest +{ + debug(string) printf("string.toStringz.unittest\n"); + + char* p = toStringz("foo"); + assert(strlen(p) == 3); + char foo[] = "abbzxyzzy"; + p = toStringz(foo[3..5]); + assert(strlen(p) == 2); + + char[] test = ""; + p = toStringz(test); + assert(*p == 0); +} + +/****************************************** + * find, ifind _find first occurrence of c in string s. + * rfind, irfind _find last occurrence of c in string s. + * + * find, rfind are case sensitive; ifind, irfind are case insensitive. + * Returns: + * Index in s where c is found, -1 if not found. + */ + +int find(char[] s, dchar c) +{ + if (c <= 0x7F) + { // Plain old ASCII + auto p = cast(char*)memchr(s.ptr, c, s.length); + if (p) + return p - cast(char *)s; + else + return -1; + } + + // c is a universal character + foreach (int i, dchar c2; s) + { + if (c == c2) + return i; + } + return -1; +} + +unittest +{ + debug(string) printf("string.find.unittest\n"); + + int i; + + i = find(null, cast(dchar)'a'); + assert(i == -1); + i = find("def", cast(dchar)'a'); + assert(i == -1); + i = find("abba", cast(dchar)'a'); + assert(i == 0); + i = find("def", cast(dchar)'f'); + assert(i == 2); +} + + +/****************************************** + * ditto + */ + +int ifind(char[] s, dchar c) +{ + char* p; + + if (c <= 0x7F) + { // Plain old ASCII + char c1 = cast(char) std.ctype.tolower(c); + + foreach (int i, char c2; s) + { + c2 = cast(char)std.ctype.tolower(c2); + if (c1 == c2) + return i; + } + } + else + { // c is a universal character + dchar c1 = std.uni.toUniLower(c); + + foreach (int i, dchar c2; s) + { + c2 = std.uni.toUniLower(c2); + if (c1 == c2) + return i; + } + } + return -1; +} + +unittest +{ + debug(string) printf("string.ifind.unittest\n"); + + int i; + + i = ifind(null, cast(dchar)'a'); + assert(i == -1); + i = ifind("def", cast(dchar)'a'); + assert(i == -1); + i = ifind("Abba", cast(dchar)'a'); + assert(i == 0); + i = ifind("def", cast(dchar)'F'); + assert(i == 2); + + char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun."; + + i = ifind("def", cast(char)'f'); + assert(i == 2); + + i = ifind(sPlts, cast(char)'P'); + assert(i == 23); + i = ifind(sPlts, cast(char)'R'); + assert(i == 2); +} + + +/****************************************** + * ditto + */ + +int rfind(char[] s, dchar c) +{ + size_t i; + + if (c <= 0x7F) + { // Plain old ASCII + for (i = s.length; i-- != 0;) + { + if (s[i] == c) + break; + } + return i; + } + + // c is a universal character + char[4] buf; + char[] t; + t = std.utf.toUTF8(buf, c); + return rfind(s, t); +} + +unittest +{ + debug(string) printf("string.rfind.unittest\n"); + + int i; + + i = rfind(null, cast(dchar)'a'); + assert(i == -1); + i = rfind("def", cast(dchar)'a'); + assert(i == -1); + i = rfind("abba", cast(dchar)'a'); + assert(i == 3); + i = rfind("def", cast(dchar)'f'); + assert(i == 2); +} + +/****************************************** + * ditto + */ + +int irfind(char[] s, dchar c) +{ + size_t i; + + if (c <= 0x7F) + { // Plain old ASCII + char c1 = cast(char) std.ctype.tolower(c); + + for (i = s.length; i-- != 0;) + { char c2 = s[i]; + + c2 = cast(char) std.ctype.tolower(c2); + if (c1 == c2) + break; + } + } + else + { // c is a universal character + dchar c1 = std.uni.toUniLower(c); + + for (i = s.length; i-- != 0;) + { char cx = s[i]; + + if (cx <= 0x7F) + continue; // skip, since c is not ASCII + if ((cx & 0xC0) == 0x80) + continue; // skip non-starting UTF-8 chars + + size_t j = i; + dchar c2 = std.utf.decode(s, j); + c2 = std.uni.toUniLower(c2); + if (c1 == c2) + break; + } + } + return i; +} + +unittest +{ + debug(string) printf("string.irfind.unittest\n"); + + int i; + + i = irfind(null, cast(dchar)'a'); + assert(i == -1); + i = irfind("def", cast(dchar)'a'); + assert(i == -1); + i = irfind("AbbA", cast(dchar)'a'); + assert(i == 3); + i = irfind("def", cast(dchar)'F'); + assert(i == 2); + + char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun."; + + i = irfind("def", cast(char)'f'); + assert(i == 2); + + i = irfind(sPlts, cast(char)'M'); + assert(i == 34); + i = irfind(sPlts, cast(char)'S'); + assert(i == 40); +} + + +/****************************************** + * find, ifind _find first occurrence of sub[] in string s[]. + * rfind, irfind _find last occurrence of sub[] in string s[]. + * + * find, rfind are case sensitive; ifind, irfind are case insensitive. + * Returns: + * Index in s where c is found, -1 if not found. + */ + +int find(char[] s, char[] sub) + out (result) + { + if (result == -1) + { + } + else + { + assert(0 <= result && result < s.length - sub.length + 1); + assert(memcmp(&s[result], sub.ptr, sub.length) == 0); + } + } + body + { + auto sublength = sub.length; + + if (sublength == 0) + return 0; + + if (s.length >= sublength) + { + auto c = sub[0]; + if (sublength == 1) + { + auto p = cast(char*)memchr(s.ptr, c, s.length); + if (p) + return p - &s[0]; + } + else + { + size_t imax = s.length - sublength + 1; + + // Remainder of sub[] + char *q = &sub[1]; + sublength--; + + for (size_t i = 0; i < imax; i++) + { + char *p = cast(char*)memchr(&s[i], c, imax - i); + if (!p) + break; + i = p - &s[0]; + if (memcmp(p + 1, q, sublength) == 0) + return i; + } + } + } + return -1; + } + + +unittest +{ + debug(string) printf("string.find.unittest\n"); + + int i; + + i = find(null, "a"); + assert(i == -1); + i = find("def", "a"); + assert(i == -1); + i = find("abba", "a"); + assert(i == 0); + i = find("def", "f"); + assert(i == 2); + i = find("dfefffg", "fff"); + assert(i == 3); + i = find("dfeffgfff", "fff"); + assert(i == 6); +} + +/****************************************** + * ditto + */ + +int ifind(char[] s, char[] sub) + out (result) + { + if (result == -1) + { + } + else + { + assert(0 <= result && result < s.length - sub.length + 1); + assert(icmp(s[result .. result + sub.length], sub) == 0); + } + } + body + { + auto sublength = sub.length; + int i; + + if (sublength == 0) + return 0; + + if (s.length < sublength) + return -1; + + auto c = sub[0]; + if (sublength == 1) + { + i = ifind(s, c); + } + else if (c <= 0x7F) + { + size_t imax = s.length - sublength + 1; + + // Remainder of sub[] + char[] subn = sub[1 .. sublength]; + + for (i = 0; i < imax; i++) + { + auto j = ifind(s[i .. imax], c); + if (j == -1) + return -1; + i += j; + if (icmp(s[i + 1 .. i + sublength], subn) == 0) + return i; + } + i = -1; + } + else + { + size_t imax = s.length - sublength; + + for (i = 0; i <= imax; i++) + { + if (icmp(s[i .. i + sublength], sub) == 0) + return i; + } + i = -1; + } + return i; + } + + +unittest +{ + debug(string) printf("string.ifind.unittest\n"); + + int i; + + i = ifind(null, "a"); + assert(i == -1); + i = ifind("def", "a"); + assert(i == -1); + i = ifind("abba", "a"); + assert(i == 0); + i = ifind("def", "f"); + assert(i == 2); + i = ifind("dfefffg", "fff"); + assert(i == 3); + i = ifind("dfeffgfff", "fff"); + assert(i == 6); + + char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun."; + char[] sMars = "Who\'s \'My Favorite Maritian?\'"; + + i = ifind(sMars, "MY fAVe"); + assert(i == -1); + i = ifind(sMars, "mY fAVOriTe"); + assert(i == 7); + i = ifind(sPlts, "mArS:"); + assert(i == 0); + i = ifind(sPlts, "rOcK"); + assert(i == 17); + i = ifind(sPlts, "Un."); + assert(i == 41); + i = ifind(sPlts, sPlts); + assert(i == 0); + + i = ifind("\u0100", "\u0100"); + assert(i == 0); + + // Thanks to Carlos Santander B. and zwang + i = ifind("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y", + "page-break-before"); + assert(i == -1); +} + +/****************************************** + * ditto + */ + +int rfind(char[] s, char[] sub) + out (result) + { + if (result == -1) + { + } + else + { + assert(0 <= result && result < s.length - sub.length + 1); + assert(memcmp(&s[0] + result, sub.ptr, sub.length) == 0); + } + } + body + { + char c; + + if (sub.length == 0) + return s.length; + c = sub[0]; + if (sub.length == 1) + return rfind(s, c); + for (int i = s.length - sub.length; i >= 0; i--) + { + if (s[i] == c) + { + if (memcmp(&s[i + 1], &sub[1], sub.length - 1) == 0) + return i; + } + } + return -1; + } + +unittest +{ + int i; + + debug(string) printf("string.rfind.unittest\n"); + i = rfind("abcdefcdef", "c"); + assert(i == 6); + i = rfind("abcdefcdef", "cd"); + assert(i == 6); + i = rfind("abcdefcdef", "x"); + assert(i == -1); + i = rfind("abcdefcdef", "xy"); + assert(i == -1); + i = rfind("abcdefcdef", ""); + assert(i == 10); +} + + +/****************************************** + * ditto + */ + +int irfind(char[] s, char[] sub) + out (result) + { + if (result == -1) + { + } + else + { + assert(0 <= result && result < s.length - sub.length + 1); + assert(icmp(s[result .. result + sub.length], sub) == 0); + } + } + body + { + dchar c; + + if (sub.length == 0) + return s.length; + c = sub[0]; + if (sub.length == 1) + return irfind(s, c); + if (c <= 0x7F) + { + c = std.ctype.tolower(c); + for (int i = s.length - sub.length; i >= 0; i--) + { + if (std.ctype.tolower(s[i]) == c) + { + if (icmp(s[i + 1 .. i + sub.length], sub[1 .. sub.length]) == 0) + return i; + } + } + } + else + { + for (int i = s.length - sub.length; i >= 0; i--) + { + if (icmp(s[i .. i + sub.length], sub) == 0) + return i; + } + } + return -1; + } + +unittest +{ + int i; + + debug(string) printf("string.irfind.unittest\n"); + i = irfind("abcdefCdef", "c"); + assert(i == 6); + i = irfind("abcdefCdef", "cD"); + assert(i == 6); + i = irfind("abcdefcdef", "x"); + assert(i == -1); + i = irfind("abcdefcdef", "xy"); + assert(i == -1); + i = irfind("abcdefcdef", ""); + assert(i == 10); + + char[] sPlts = "Mars: the fourth Rock (Planet) from the Sun."; + char[] sMars = "Who\'s \'My Favorite Maritian?\'"; + + i = irfind("abcdefcdef", "c"); + assert(i == 6); + i = irfind("abcdefcdef", "cd"); + assert(i == 6); + i = irfind( "abcdefcdef", "def" ); + assert(i == 7); + + i = irfind(sMars, "RiTE maR"); + assert(i == 14); + i = irfind(sPlts, "FOuRTh"); + assert(i == 10); + i = irfind(sMars, "whO\'s \'MY"); + assert(i == 0); + i = irfind(sMars, sMars); + assert(i == 0); +} + + +/************************************ + * Convert string s[] to lower case. + */ + +string tolower(string s) +{ + int changed; + char[] r; + + for (size_t i = 0; i < s.length; i++) + { + auto c = s[i]; + if ('A' <= c && c <= 'Z') + { + if (!changed) + { + r = s.dup; + changed = 1; + } + r[i] = cast(char) (c + (cast(char)'a' - 'A')); + } + else if (c > 0x7F) + { + foreach (size_t j, dchar dc; s[i .. length]) + { + if (std.uni.isUniUpper(dc)) + { + dc = std.uni.toUniLower(dc); + if (!changed) + { + r = s[0 .. i + j].dup; + changed = 2; + } + } + if (changed) + { + if (changed == 1) + { r = r[0 .. i + j]; + changed = 2; + } + std.utf.encode(r, dc); + } + } + break; + } + } + return changed ? r : s; +} + +unittest +{ + debug(string) printf("string.tolower.unittest\n"); + + char[] s1 = "FoL"; + char[] s2; + + s2 = tolower(s1); + assert(cmp(s2, "fol") == 0); + assert(s2 != s1); + + s1 = "A\u0100B\u0101d"; + s2 = tolower(s1); + assert(cmp(s2, "a\u0101b\u0101d") == 0); + assert(s2 !is s1); + + s1 = "A\u0460B\u0461d"; + s2 = tolower(s1); + assert(cmp(s2, "a\u0461b\u0461d") == 0); + assert(s2 !is s1); + + s1 = "\u0130"; + s2 = tolower(s1); + assert(s2 == "i"); + assert(s2 !is s1); +} + +/************************************ + * Convert string s[] to upper case. + */ + +string toupper(string s) +{ + int changed; + char[] r; + + for (size_t i = 0; i < s.length; i++) + { + auto c = s[i]; + if ('a' <= c && c <= 'z') + { + if (!changed) + { + r = s.dup; + changed = 1; + } + r[i] = cast(char) (c - (cast(char)'a' - 'A')); + } + else if (c > 0x7F) + { + foreach (size_t j, dchar dc; s[i .. length]) + { + if (std.uni.isUniLower(dc)) + { + dc = std.uni.toUniUpper(dc); + if (!changed) + { + r = s[0 .. i + j].dup; + changed = 2; + } + } + if (changed) + { + if (changed == 1) + { r = r[0 .. i + j]; + changed = 2; + } + std.utf.encode(r, dc); + } + } + break; + } + } + return changed ? r : s; +} + +unittest +{ + debug(string) printf("string.toupper.unittest\n"); + + char[] s1 = "FoL"; + char[] s2; + + s2 = toupper(s1); + assert(cmp(s2, "FOL") == 0); + assert(s2 !is s1); + + s1 = "a\u0100B\u0101d"; + s2 = toupper(s1); + assert(cmp(s2, "A\u0100B\u0100D") == 0); + assert(s2 !is s1); + + s1 = "a\u0460B\u0461d"; + s2 = toupper(s1); + assert(cmp(s2, "A\u0460B\u0460D") == 0); + assert(s2 !is s1); +} + + +/******************************************** + * Capitalize first character of string s[], convert rest of string s[] + * to lower case. + */ + +char[] capitalize(char[] s) +{ + int changed; + int i; + char[] r = s; + + changed = 0; + + foreach (size_t i, dchar c; s) + { dchar c2; + + if (i == 0) + { + c2 = std.uni.toUniUpper(c); + if (c != c2) + { + changed = 1; + r = null; + } + } + else + { + c2 = std.uni.toUniLower(c); + if (c != c2) + { + if (!changed) + { changed = 1; + r = s[0 .. i].dup; + } + } + } + if (changed) + std.utf.encode(r, c2); + } + return r; +} + + +unittest +{ + debug(string) printf("string.toupper.capitalize\n"); + + char[] s1 = "FoL"; + char[] s2; + + s2 = capitalize(s1); + assert(cmp(s2, "Fol") == 0); + assert(s2 !is s1); + + s2 = capitalize(s1[0 .. 2]); + assert(cmp(s2, "Fo") == 0); + assert(s2.ptr == s1.ptr); + + s1 = "fOl"; + s2 = capitalize(s1); + assert(cmp(s2, "Fol") == 0); + assert(s2 !is s1); +} + + +/******************************************** + * Capitalize all words in string s[]. + * Remove leading and trailing whitespace. + * Replace all sequences of whitespace with a single space. + */ + +char[] capwords(char[] s) +{ + char[] r; + bool inword = false; + size_t istart = 0; + size_t i; + + for (i = 0; i < s.length; i++) + { + switch (s[i]) + { + case ' ': + case '\t': + case '\f': + case '\r': + case '\n': + case '\v': + if (inword) + { + r ~= capitalize(s[istart .. i]); + inword = false; + } + break; + + default: + if (!inword) + { + if (r.length) + r ~= ' '; + istart = i; + inword = true; + } + break; + } + } + if (inword) + { + r ~= capitalize(s[istart .. i]); + } + + return r; +} + + +unittest +{ + debug(string) printf("string.capwords.unittest\n"); + + char[] s1 = "\tfoo abc(aD)* \t (q PTT "; + char[] s2; + + s2 = capwords(s1); + //writefln("s2 = '%s'", s2); + assert(cmp(s2, "Foo Abc(ad)* (q Ptt") == 0); +} + +/******************************************** + * Return a string that consists of s[] repeated n times. + */ + +char[] repeat(char[] s, size_t n) +{ + if (n == 0) + return null; + if (n == 1) + return s; + char[] r = new char[n * s.length]; + if (s.length == 1) + r[] = s[0]; + else + { auto len = s.length; + + for (size_t i = 0; i < n * len; i += len) + { + r[i .. i + len] = s[]; + } + } + return r; +} + + +unittest +{ + debug(string) printf("string.repeat.unittest\n"); + + char[] s; + + s = repeat("1234", 0); + assert(s is null); + s = repeat("1234", 1); + assert(cmp(s, "1234") == 0); + s = repeat("1234", 2); + assert(cmp(s, "12341234") == 0); + s = repeat("1", 4); + assert(cmp(s, "1111") == 0); + s = repeat(null, 4); + assert(s is null); +} + + +/******************************************** + * Concatenate all the strings in words[] together into one + * string; use sep[] as the separator. + */ + +char[] join(char[][] words, char[] sep) +{ + char[] result; + + if (words.length) + { + size_t len = 0; + size_t i; + + for (i = 0; i < words.length; i++) + len += words[i].length; + + auto seplen = sep.length; + len += (words.length - 1) * seplen; + + result = new char[len]; + + size_t j; + i = 0; + while (true) + { + uint wlen = words[i].length; + + result[j .. j + wlen] = words[i]; + j += wlen; + i++; + if (i >= words.length) + break; + result[j .. j + seplen] = sep; + j += seplen; + } + assert(j == len); + } + return result; +} + +unittest +{ + debug(string) printf("string.join.unittest\n"); + + char[] word1 = "peter"; + char[] word2 = "paul"; + char[] word3 = "jerry"; + char[][3] words; + char[] r; + int i; + + words[0] = word1; + words[1] = word2; + words[2] = word3; + r = join(words, ","); + i = cmp(r, "peter,paul,jerry"); + assert(i == 0); +} + + +/************************************** + * Split s[] into an array of words, + * using whitespace as the delimiter. + */ + +char[][] split(char[] s) +{ + size_t i; + size_t istart = 0; + bool inword = false; + char[][] words; + + for (i = 0; i < s.length; i++) + { + switch (s[i]) + { + case ' ': + case '\t': + case '\f': + case '\r': + case '\n': + case '\v': + if (inword) + { + words ~= s[istart .. i]; + inword = false; + } + break; + + default: + if (!inword) + { istart = i; + inword = true; + } + break; + } + } + if (inword) + words ~= s[istart .. i]; + return words; +} + +unittest +{ + debug(string) printf("string.split1\n"); + + char[] s = " peter paul\tjerry "; + char[][] words; + int i; + + words = split(s); + assert(words.length == 3); + i = cmp(words[0], "peter"); + assert(i == 0); + i = cmp(words[1], "paul"); + assert(i == 0); + i = cmp(words[2], "jerry"); + assert(i == 0); +} + + +/************************************** + * Split s[] into an array of words, + * using delim[] as the delimiter. + */ + +char[][] split(char[] s, char[] delim) + in + { + assert(delim.length > 0); + } + body + { + size_t i; + size_t j; + char[][] words; + + i = 0; + if (s.length) + { + if (delim.length == 1) + { char c = delim[0]; + size_t nwords = 0; + char* p = &s[0]; + char* pend = p + s.length; + + while (true) + { + nwords++; + p = cast(char*)memchr(p, c, pend - p); + if (!p) + break; + p++; + if (p == pend) + { nwords++; + break; + } + } + words.length = nwords; + + int wordi = 0; + i = 0; + while (true) + { + p = cast(char*)memchr(&s[i], c, s.length - i); + if (!p) + { + words[wordi] = s[i .. s.length]; + break; + } + j = p - &s[0]; + words[wordi] = s[i .. j]; + wordi++; + i = j + 1; + if (i == s.length) + { + words[wordi] = ""; + break; + } + } + assert(wordi + 1 == nwords); + } + else + { size_t nwords = 0; + + while (true) + { + nwords++; + j = find(s[i .. s.length], delim); + if (j == -1) + break; + i += j + delim.length; + if (i == s.length) + { nwords++; + break; + } + assert(i < s.length); + } + words.length = nwords; + + int wordi = 0; + i = 0; + while (true) + { + j = find(s[i .. s.length], delim); + if (j == -1) + { + words[wordi] = s[i .. s.length]; + break; + } + words[wordi] = s[i .. i + j]; + wordi++; + i += j + delim.length; + if (i == s.length) + { + words[wordi] = ""; + break; + } + assert(i < s.length); + } + assert(wordi + 1 == nwords); + } + } + return words; + } + +unittest +{ + debug(string) printf("string.split2\n"); + + char[] s = ",peter,paul,jerry,"; + char[][] words; + int i; + + words = split(s, ","); + assert(words.length == 5); + i = cmp(words[0], ""); + assert(i == 0); + i = cmp(words[1], "peter"); + assert(i == 0); + i = cmp(words[2], "paul"); + assert(i == 0); + i = cmp(words[3], "jerry"); + assert(i == 0); + i = cmp(words[4], ""); + assert(i == 0); + + s = s[0 .. s.length - 1]; // lop off trailing ',' + words = split(s, ","); + assert(words.length == 4); + i = cmp(words[3], "jerry"); + assert(i == 0); + + s = s[1 .. s.length]; // lop off leading ',' + words = split(s, ","); + assert(words.length == 3); + i = cmp(words[0], "peter"); + assert(i == 0); + + char[] s2 = ",,peter,,paul,,jerry,,"; + + words = split(s2, ",,"); + //printf("words.length = %d\n", words.length); + assert(words.length == 5); + i = cmp(words[0], ""); + assert(i == 0); + i = cmp(words[1], "peter"); + assert(i == 0); + i = cmp(words[2], "paul"); + assert(i == 0); + i = cmp(words[3], "jerry"); + assert(i == 0); + i = cmp(words[4], ""); + assert(i == 0); + + s2 = s2[0 .. s2.length - 2]; // lop off trailing ',,' + words = split(s2, ",,"); + assert(words.length == 4); + i = cmp(words[3], "jerry"); + assert(i == 0); + + s2 = s2[2 .. s2.length]; // lop off leading ',,' + words = split(s2, ",,"); + assert(words.length == 3); + i = cmp(words[0], "peter"); + assert(i == 0); +} + + +/************************************** + * Split s[] into an array of lines, + * using CR, LF, or CR-LF as the delimiter. + * The delimiter is not included in the line. + */ + +char[][] splitlines(char[] s) +{ + uint i; + uint istart; + uint nlines; + char[][] lines; + + nlines = 0; + for (i = 0; i < s.length; i++) + { char c; + + c = s[i]; + if (c == '\r' || c == '\n') + { + nlines++; + istart = i + 1; + if (c == '\r' && i + 1 < s.length && s[i + 1] == '\n') + { + i++; + istart++; + } + } + } + if (istart != i) + nlines++; + + lines = new char[][nlines]; + nlines = 0; + istart = 0; + for (i = 0; i < s.length; i++) + { char c; + + c = s[i]; + if (c == '\r' || c == '\n') + { + lines[nlines] = s[istart .. i]; + nlines++; + istart = i + 1; + if (c == '\r' && i + 1 < s.length && s[i + 1] == '\n') + { + i++; + istart++; + } + } + } + if (istart != i) + { lines[nlines] = s[istart .. i]; + nlines++; + } + + assert(nlines == lines.length); + return lines; +} + +unittest +{ + debug(string) printf("string.splitlines\n"); + + char[] s = "\rpeter\n\rpaul\r\njerry\n"; + char[][] lines; + int i; + + lines = splitlines(s); + //printf("lines.length = %d\n", lines.length); + assert(lines.length == 5); + //printf("lines[0] = %llx, '%.*s'\n", lines[0], lines[0]); + assert(lines[0].length == 0); + i = cmp(lines[1], "peter"); + assert(i == 0); + assert(lines[2].length == 0); + i = cmp(lines[3], "paul"); + assert(i == 0); + i = cmp(lines[4], "jerry"); + assert(i == 0); + + s = s[0 .. s.length - 1]; // lop off trailing \n + lines = splitlines(s); + //printf("lines.length = %d\n", lines.length); + assert(lines.length == 5); + i = cmp(lines[4], "jerry"); + assert(i == 0); +} + + +/***************************************** + * Strips leading or trailing whitespace, or both. + */ + +char[] stripl(char[] s) +{ + uint i; + + for (i = 0; i < s.length; i++) + { + if (!std.ctype.isspace(s[i])) + break; + } + return s[i .. s.length]; +} + +char[] stripr(char[] s) /// ditto +{ + uint i; + + for (i = s.length; i > 0; i--) + { + if (!std.ctype.isspace(s[i - 1])) + break; + } + return s[0 .. i]; +} + +char[] strip(char[] s) /// ditto +{ + return stripr(stripl(s)); +} + +unittest +{ + debug(string) printf("string.strip.unittest\n"); + char[] s; + int i; + + s = strip(" foo\t "); + i = cmp(s, "foo"); + assert(i == 0); +} + +/******************************************* + * Returns s[] sans trailing delimiter[], if any. + * If delimiter[] is null, removes trailing CR, LF, or CRLF, if any. + */ + +char[] chomp(char[] s, char[] delimiter = null) +{ + if (delimiter is null) + { auto len = s.length; + + if (len) + { auto c = s[len - 1]; + + if (c == '\r') // if ends in CR + len--; + else if (c == '\n') // if ends in LF + { + len--; + if (len && s[len - 1] == '\r') + len--; // remove CR-LF + } + } + return s[0 .. len]; + } + else if (s.length >= delimiter.length) + { + if (s[length - delimiter.length .. length] == delimiter) + return s[0 .. length - delimiter.length]; + } + return s; +} + +unittest +{ + debug(string) printf("string.chomp.unittest\n"); + char[] s; + + s = chomp(null); + assert(s is null); + s = chomp("hello"); + assert(s == "hello"); + s = chomp("hello\n"); + assert(s == "hello"); + s = chomp("hello\r"); + assert(s == "hello"); + s = chomp("hello\r\n"); + assert(s == "hello"); + s = chomp("hello\n\r"); + assert(s == "hello\n"); + s = chomp("hello\n\n"); + assert(s == "hello\n"); + s = chomp("hello\r\r"); + assert(s == "hello\r"); + s = chomp("hello\nxxx\n"); + assert(s == "hello\nxxx"); + + s = chomp(null, null); + assert(s is null); + s = chomp("hello", "o"); + assert(s == "hell"); + s = chomp("hello", "p"); + assert(s == "hello"); + s = chomp("hello", null); + assert(s == "hello"); + s = chomp("hello", "llo"); + assert(s == "he"); +} + + +/*********************************************** + * Returns s[] sans trailing character, if there is one. + * If last two characters are CR-LF, then both are removed. + */ + +char[] chop(char[] s) +{ auto len = s.length; + + if (len) + { + if (len >= 2 && s[len - 1] == '\n' && s[len - 2] == '\r') + return s[0 .. len - 2]; + + // If we're in a tail of a UTF-8 sequence, back up + while ((s[len - 1] & 0xC0) == 0x80) + { + len--; + if (len == 0) + throw new std.utf.UtfException("invalid UTF sequence", 0); + } + + return s[0 .. len - 1]; + } + return s; +} + + +unittest +{ + debug(string) printf("string.chop.unittest\n"); + char[] s; + + s = chop(null); + assert(s is null); + s = chop("hello"); + assert(s == "hell"); + s = chop("hello\r\n"); + assert(s == "hello"); + s = chop("hello\n\r"); + assert(s == "hello\n"); +} + + +/******************************************* + * Left justify, right justify, or center string s[] + * in field width chars wide. + */ + +char[] ljustify(char[] s, int width) +{ + if (s.length >= width) + return s; + char[] r = new char[width]; + r[0..s.length] = s; + r[s.length .. width] = cast(char)' '; + return r; +} + +/// ditto +char[] rjustify(char[] s, int width) +{ + if (s.length >= width) + return s; + char[] r = new char[width]; + r[0 .. width - s.length] = cast(char)' '; + r[width - s.length .. width] = s; + return r; +} + +/// ditto +char[] center(char[] s, int width) +{ + if (s.length >= width) + return s; + char[] r = new char[width]; + int left = (width - s.length) / 2; + r[0 .. left] = cast(char)' '; + r[left .. left + s.length] = s; + r[left + s.length .. width] = cast(char)' '; + return r; +} + +unittest +{ + debug(string) printf("string.justify.unittest\n"); + + char[] s = "hello"; + char[] r; + int i; + + r = ljustify(s, 8); + i = cmp(r, "hello "); + assert(i == 0); + + r = rjustify(s, 8); + i = cmp(r, " hello"); + assert(i == 0); + + r = center(s, 8); + i = cmp(r, " hello "); + assert(i == 0); + + r = zfill(s, 8); + i = cmp(r, "000hello"); + assert(i == 0); +} + + +/***************************************** + * Same as rjustify(), but fill with '0's. + */ + +char[] zfill(char[] s, int width) +{ + if (s.length >= width) + return s; + char[] r = new char[width]; + r[0 .. width - s.length] = cast(char)'0'; + r[width - s.length .. width] = s; + return r; +} + +/******************************************** + * Replace occurrences of from[] with to[] in s[]. + */ + +char[] replace(char[] s, char[] from, char[] to) +{ + char[] p; + int i; + size_t istart; + + //printf("replace('%.*s','%.*s','%.*s')\n", s, from, to); + if (from.length == 0) + return s; + istart = 0; + while (istart < s.length) + { + i = find(s[istart .. s.length], from); + if (i == -1) + { + p ~= s[istart .. s.length]; + break; + } + p ~= s[istart .. istart + i]; + p ~= to; + istart += i + from.length; + } + return p; +} + +unittest +{ + debug(string) printf("string.replace.unittest\n"); + + char[] s = "This is a foo foo list"; + char[] from = "foo"; + char[] to = "silly"; + char[] r; + int i; + + r = replace(s, from, to); + i = cmp(r, "This is a silly silly list"); + assert(i == 0); + + r = replace(s, "", to); + i = cmp(r, "This is a foo foo list"); + assert(i == 0); +} + +/***************************** + * Return a _string that is string[] with slice[] replaced by replacement[]. + */ + +char[] replaceSlice(char[] string, char[] slice, char[] replacement) +in +{ + // Verify that slice[] really is a slice of string[] + int so = cast(char*)slice - cast(char*)string; + assert(so >= 0); + //printf("string.length = %d, so = %d, slice.length = %d\n", string.length, so, slice.length); + assert(string.length >= so + slice.length); +} +body +{ + char[] result; + int so = cast(char*)slice - cast(char*)string; + + result.length = string.length - slice.length + replacement.length; + + result[0 .. so] = string[0 .. so]; + result[so .. so + replacement.length] = replacement; + result[so + replacement.length .. result.length] = string[so + slice.length .. string.length]; + + return result; +} + +unittest +{ + debug(string) printf("string.replaceSlice.unittest\n"); + + char[] string = "hello"; + char[] slice = string[2 .. 4]; + + char[] r = replaceSlice(string, slice, "bar"); + int i; + i = cmp(r, "hebaro"); + assert(i == 0); +} + +/********************************************** + * Insert sub[] into s[] at location index. + */ + +char[] insert(char[] s, size_t index, char[] sub) +in +{ + assert(0 <= index && index <= s.length); +} +body +{ + if (sub.length == 0) + return s; + + if (s.length == 0) + return sub; + + int newlength = s.length + sub.length; + char[] result = new char[newlength]; + + result[0 .. index] = s[0 .. index]; + result[index .. index + sub.length] = sub; + result[index + sub.length .. newlength] = s[index .. s.length]; + return result; +} + +unittest +{ + debug(string) printf("string.insert.unittest\n"); + + char[] r; + int i; + + r = insert("abcd", 0, "e"); + i = cmp(r, "eabcd"); + assert(i == 0); + + r = insert("abcd", 4, "e"); + i = cmp(r, "abcde"); + assert(i == 0); + + r = insert("abcd", 2, "ef"); + i = cmp(r, "abefcd"); + assert(i == 0); + + r = insert(null, 0, "e"); + i = cmp(r, "e"); + assert(i == 0); + + r = insert("abcd", 0, null); + i = cmp(r, "abcd"); + assert(i == 0); +} + +/*********************************************** + * Count up all instances of sub[] in s[]. + */ + +size_t count(char[] s, char[] sub) +{ + size_t i; + int j; + int count = 0; + + for (i = 0; i < s.length; i += j + sub.length) + { + j = find(s[i .. s.length], sub); + if (j == -1) + break; + count++; + } + return count; +} + +unittest +{ + debug(string) printf("string.count.unittest\n"); + + char[] s = "This is a fofofof list"; + char[] sub = "fof"; + int i; + + i = count(s, sub); + assert(i == 2); +} + + +/************************************************ + * Replace tabs with the appropriate number of spaces. + * tabsize is the distance between tab stops. + */ + +char[] expandtabs(char[] string, int tabsize = 8) +{ + bool changes = false; + char[] result = string; + int column; + int nspaces; + + foreach (size_t i, dchar c; string) + { + switch (c) + { + case '\t': + nspaces = tabsize - (column % tabsize); + if (!changes) + { + changes = true; + result = null; + result.length = string.length + nspaces - 1; + result.length = i + nspaces; + result[0 .. i] = string[0 .. i]; + result[i .. i + nspaces] = ' '; + } + else + { int j = result.length; + result.length = j + nspaces; + result[j .. j + nspaces] = ' '; + } + column += nspaces; + break; + + case '\r': + case '\n': + case PS: + case LS: + column = 0; + goto L1; + + default: + column++; + L1: + if (changes) + { + if (c <= 0x7F) + result ~= cast(char)c; + else + std.utf.encode(result, c); + } + break; + } + } + + return result; +} + +unittest +{ + debug(string) printf("string.expandtabs.unittest\n"); + + char[] s = "This \tis\t a fofof\tof list"; + char[] r; + int i; + + r = expandtabs(s, 8); + i = cmp(r, "This is a fofof of list"); + assert(i == 0); + + r = expandtabs(null); + assert(r == null); + r = expandtabs(""); + assert(r.length == 0); + r = expandtabs("a"); + assert(r == "a"); + r = expandtabs("\t"); + assert(r == " "); + r = expandtabs( " ab\tasdf "); + //writefln("r = '%s'", r); + assert(r == " ab asdf "); + // TODO: need UTF test case +} + + +/******************************************* + * Replace spaces in string with the optimal number of tabs. + * Trailing spaces or tabs in a line are removed. + * Params: + * string = String to convert. + * tabsize = Tab columns are tabsize spaces apart. tabsize defaults to 8. + */ + +char[] entab(char[] string, int tabsize = 8) +{ + bool changes = false; + char[] result = string; + + int nspaces = 0; + int nwhite = 0; + int column = 0; // column number + + foreach (size_t i, dchar c; string) + { + + void change() + { + changes = true; + result = null; + result.length = string.length; + result.length = i; + result[0 .. i] = string[0 .. i]; + } + + switch (c) + { + case '\t': + nwhite++; + if (nspaces) + { + if (!changes) + change(); + + int j = result.length - nspaces; + int ntabs = (((column - nspaces) % tabsize) + nspaces) / tabsize; + result.length = j + ntabs; + result[j .. j + ntabs] = '\t'; + nwhite += ntabs - nspaces; + nspaces = 0; + } + column = (column + tabsize) / tabsize * tabsize; + break; + + case '\r': + case '\n': + case PS: + case LS: + // Truncate any trailing spaces or tabs + if (nwhite) + { + if (!changes) + change(); + result = result[0 .. result.length - nwhite]; + } + break; + + default: + if (nspaces >= 2 && (column % tabsize) == 0) + { + if (!changes) + change(); + + int j = result.length - nspaces; + int ntabs = (nspaces + tabsize - 1) / tabsize; + result.length = j + ntabs; + result[j .. j + ntabs] = '\t'; + nwhite += ntabs - nspaces; + nspaces = 0; + } + if (c == ' ') + { nwhite++; + nspaces++; + } + else + { nwhite = 0; + nspaces = 0; + } + column++; + break; + } + if (changes) + { + if (c <= 0x7F) + result ~= cast(char)c; + else + std.utf.encode(result, c); + } + } + + // Truncate any trailing spaces or tabs + if (nwhite) + result = result[0 .. result.length - nwhite]; + + return result; +} + +unittest +{ + debug(string) printf("string.entab.unittest\n"); + + char[] r; + + r = entab(null); + assert(r == null); + r = entab(""); + assert(r.length == 0); + r = entab("a"); + assert(r == "a"); + r = entab(" "); + assert(r == ""); + r = entab(" x"); + assert(r == "\tx"); + r = entab(" ab asdf "); + assert(r == " ab\tasdf"); + r = entab(" ab asdf "); + assert(r == " ab\t asdf"); + r = entab(" ab \t asdf "); + assert(r == " ab\t asdf"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\ta"); + r = entab("1234567 \ta"); + assert(r == "1234567\t\t\ta"); + // TODO: need UTF test case +} + + + +/************************************ + * Construct translation table for translate(). + * BUG: only works with ASCII + */ + +char[] maketrans(char[] from, char[] to) + in + { + assert(from.length == to.length); + assert(from.length <= 128); + foreach (char c; from) + { + assert(c <= 0x7F); + } + foreach (char c; to) + { + assert(c <= 0x7F); + } + } + body + { + char[] t = new char[256]; + int i; + + for (i = 0; i < t.length; i++) + t[i] = cast(char)i; + + for (i = 0; i < from.length; i++) + t[from[i]] = to[i]; + + return t; + } + +/****************************************** + * Translate characters in s[] using table created by maketrans(). + * Delete chars in delchars[]. + * BUG: only works with ASCII + */ + +char[] translate(char[] s, char[] transtab, char[] delchars) + in + { + assert(transtab.length == 256); + } + body + { + char[] r; + int count; + bool[256] deltab; + + deltab[] = false; + foreach (char c; delchars) + { + deltab[c] = true; + } + + count = 0; + foreach (char c; s) + { + if (!deltab[c]) + count++; + //printf("s[%d] = '%c', count = %d\n", i, s[i], count); + } + + r = new char[count]; + count = 0; + foreach (char c; s) + { + if (!deltab[c]) + { + r[count] = transtab[c]; + count++; + } + } + + return r; + } + +unittest +{ + debug(string) printf("string.translate.unittest\n"); + + char[] from = "abcdef"; + char[] to = "ABCDEF"; + char[] s = "The quick dog fox"; + char[] t; + char[] r; + int i; + + t = maketrans(from, to); + r = translate(s, t, "kg"); + //printf("r = '%.*s'\n", r); + i = cmp(r, "ThE quiC Do Fox"); + assert(i == 0); +} + +/*********************************************** + * Convert to char[]. + */ + +char[] toString(bool b) +{ + return b ? "true" : "false"; +} + +/// ditto +char[] toString(char c) +{ + char[] result = new char[2]; + result[0] = c; + result[1] = 0; + return result[0 .. 1]; +} + +unittest +{ + debug(string) printf("string.toString(char).unittest\n"); + + char[] s = "foo"; + char[] s2; + foreach (char c; s) + { + s2 ~= std.string.toString(c); + } + //printf("%.*s", s2); + assert(s2 == "foo"); +} + +char[] toString(ubyte ub) { return toString(cast(uint) ub); } /// ditto +char[] toString(ushort us) { return toString(cast(uint) us); } /// ditto + +/// ditto +char[] toString(uint u) +{ char[uint.sizeof * 3] buffer = void; + int ndigits; + char[] result; + + ndigits = 0; + if (u < 10) + // Avoid storage allocation for simple stuff + result = digits[u .. u + 1]; + else + { + while (u) + { + uint c = (u % 10) + '0'; + u /= 10; + ndigits++; + buffer[buffer.length - ndigits] = cast(char)c; + } + result = new char[ndigits]; + result[] = buffer[buffer.length - ndigits .. buffer.length]; + } + return result; +} + +unittest +{ + debug(string) printf("string.toString(uint).unittest\n"); + + char[] r; + int i; + + r = toString(0u); + i = cmp(r, "0"); + assert(i == 0); + + r = toString(9u); + i = cmp(r, "9"); + assert(i == 0); + + r = toString(123u); + i = cmp(r, "123"); + assert(i == 0); +} + +/// ditto +char[] toString(ulong u) +{ char[ulong.sizeof * 3] buffer; + int ndigits; + char[] result; + + if (u < 0x1_0000_0000) + return toString(cast(uint)u); + ndigits = 0; + while (u) + { + char c = cast(char)((u % 10) + '0'); + u /= 10; + ndigits++; + buffer[buffer.length - ndigits] = c; + } + result = new char[ndigits]; + result[] = buffer[buffer.length - ndigits .. buffer.length]; + return result; +} + +unittest +{ + debug(string) printf("string.toString(ulong).unittest\n"); + + char[] r; + int i; + + r = toString(0uL); + i = cmp(r, "0"); + assert(i == 0); + + r = toString(9uL); + i = cmp(r, "9"); + assert(i == 0); + + r = toString(123uL); + i = cmp(r, "123"); + assert(i == 0); +} + +char[] toString(byte b) { return toString(cast(int) b); } /// ditto +char[] toString(short s) { return toString(cast(int) s); } /// ditto + +/// ditto +char[] toString(int i) +{ char[1 + int.sizeof * 3] buffer; + char[] result; + + if (i >= 0) + return toString(cast(uint)i); + + uint u = -i; + int ndigits = 1; + while (u) + { + char c = cast(char)((u % 10) + '0'); + u /= 10; + buffer[buffer.length - ndigits] = c; + ndigits++; + } + buffer[buffer.length - ndigits] = '-'; + result = new char[ndigits]; + result[] = buffer[buffer.length - ndigits .. buffer.length]; + return result; +} + +unittest +{ + debug(string) printf("string.toString(int).unittest\n"); + + char[] r; + int i; + + r = toString(0); + i = cmp(r, "0"); + assert(i == 0); + + r = toString(9); + i = cmp(r, "9"); + assert(i == 0); + + r = toString(123); + i = cmp(r, "123"); + assert(i == 0); + + r = toString(-0); + i = cmp(r, "0"); + assert(i == 0); + + r = toString(-9); + i = cmp(r, "-9"); + assert(i == 0); + + r = toString(-123); + i = cmp(r, "-123"); + assert(i == 0); +} + +/// ditto +char[] toString(long i) +{ char[1 + long.sizeof * 3] buffer; + char[] result; + + if (i >= 0) + return toString(cast(ulong)i); + if (cast(int)i == i) + return toString(cast(int)i); + + ulong u = cast(ulong)(-i); + int ndigits = 1; + while (u) + { + char c = cast(char)((u % 10) + '0'); + u /= 10; + buffer[buffer.length - ndigits] = c; + ndigits++; + } + buffer[buffer.length - ndigits] = '-'; + result = new char[ndigits]; + result[] = buffer[buffer.length - ndigits .. buffer.length]; + return result; +} + +unittest +{ + debug(string) printf("string.toString(long).unittest\n"); + + char[] r; + int i; + + r = toString(0L); + i = cmp(r, "0"); + assert(i == 0); + + r = toString(9L); + i = cmp(r, "9"); + assert(i == 0); + + r = toString(123L); + i = cmp(r, "123"); + assert(i == 0); + + r = toString(-0L); + i = cmp(r, "0"); + assert(i == 0); + + r = toString(-9L); + i = cmp(r, "-9"); + assert(i == 0); + + r = toString(-123L); + i = cmp(r, "-123"); + assert(i == 0); +} + +/// ditto +char[] toString(float f) { return toString(cast(double) f); } + +/// ditto +char[] toString(double d) +{ + char[20] buffer; + + int len = sprintf(buffer.ptr, "%g", d); + return buffer[0 .. len].dup; +} + +/// ditto +char[] toString(real r) +{ + char[20] buffer; + + int len = sprintf(buffer.ptr, "%Lg", r); + return buffer[0 .. len].dup; +} + +/// ditto +char[] toString(ifloat f) { return toString(cast(idouble) f); } + +/// ditto +char[] toString(idouble d) +{ + char[21] buffer; + + int len = sprintf(buffer.ptr, "%gi", d); + return buffer[0 .. len].dup; +} + +/// ditto +char[] toString(ireal r) +{ + char[21] buffer; + + int len = sprintf(buffer.ptr, "%Lgi", r); + return buffer[0 .. len].dup; +} + +/// ditto +char[] toString(cfloat f) { return toString(cast(cdouble) f); } + +/// ditto +char[] toString(cdouble d) +{ + char[20 + 1 + 20 + 1] buffer; + + int len = sprintf(buffer.ptr, "%g+%gi", d.re, d.im); + return buffer[0 .. len].dup; +} + +/// ditto +char[] toString(creal r) +{ + char[20 + 1 + 20 + 1] buffer; + + int len = sprintf(buffer.ptr, "%Lg+%Lgi", r.re, r.im); + return buffer[0 .. len].dup; +} + + +/****************************************** + * Convert value to string in _radix radix. + * + * radix must be a value from 2 to 36. + * value is treated as a signed value only if radix is 10. + * The characters A through Z are used to represent values 10 through 36. + */ +char[] toString(long value, uint radix) +in +{ + assert(radix >= 2 && radix <= 36); +} +body +{ + if (radix == 10) + return toString(value); // handle signed cases only for radix 10 + return toString(cast(ulong)value, radix); +} + +/// ditto +char[] toString(ulong value, uint radix) +in +{ + assert(radix >= 2 && radix <= 36); +} +body +{ + char[value.sizeof * 8] buffer; + uint i = buffer.length; + + if (value < radix && value < hexdigits.length) + return hexdigits[cast(size_t)value .. cast(size_t)value + 1]; + + do + { ubyte c; + + c = cast(ubyte)(value % radix); + value = value / radix; + i--; + buffer[i] = cast(char)((c < 10) ? c + '0' : c + 'A' - 10); + } while (value); + return buffer[i .. length].dup; +} + +unittest +{ + debug(string) printf("string.toString(ulong, uint).unittest\n"); + + char[] r; + int i; + + r = toString(-10L, 10u); + assert(r == "-10"); + + r = toString(15L, 2u); + //writefln("r = '%s'", r); + assert(r == "1111"); + + r = toString(1L, 2u); + //writefln("r = '%s'", r); + assert(r == "1"); + + r = toString(0x1234AFL, 16u); + //writefln("r = '%s'", r); + assert(r == "1234AF"); +} + +/************************************************* + * Convert C-style 0 terminated string s to char[] string. + */ + +char[] toString(char *s) +{ + return s ? s[0 .. strlen(s)] : cast(char[])null; +} + +unittest +{ + debug(string) printf("string.toString(char*).unittest\n"); + + char[] r; + int i; + + r = toString(null); + i = cmp(r, ""); + assert(i == 0); + + r = toString("foo\0"); + i = cmp(r, "foo"); + assert(i == 0); +} + + +/***************************************************** + * Format arguments into a string. + */ + + +char[] format(...) +{ + char[] s; + + void putc(dchar c) + { + std.utf.encode(s, c); + } + + std.format.doFormat(&putc, _arguments, _argptr); + return s; +} + + +/***************************************************** + * Format arguments into string s which must be large + * enough to hold the result. Throws ArrayBoundsError if it is not. + * Returns: s + */ +char[] sformat(char[] s, ...) +{ size_t i; + + void putc(dchar c) + { + if (c <= 0x7F) + { + if (i >= s.length) + throw new ArrayBoundsError("std.string.sformat", 0); + s[i] = cast(char)c; + ++i; + } + else + { char[4] buf; + char[] b; + + b = std.utf.toUTF8(buf, c); + if (i + b.length > s.length) + throw new ArrayBoundsError("std.string.sformat", 0); + s[i..i+b.length] = b[]; + i += b.length; + } + } + + std.format.doFormat(&putc, _arguments, _argptr); + return s[0 .. i]; +} + + +unittest +{ + debug(string) printf("std.string.format.unittest\n"); + + char[] r; + int i; +/+ + r = format(null); + i = cmp(r, ""); + assert(i == 0); ++/ + r = format("foo"); + i = cmp(r, "foo"); + assert(i == 0); + + r = format("foo%%"); + i = cmp(r, "foo%"); + assert(i == 0); + + r = format("foo%s", 'C'); + i = cmp(r, "fooC"); + assert(i == 0); + + r = format("%s foo", "bar"); + i = cmp(r, "bar foo"); + assert(i == 0); + + r = format("%s foo %s", "bar", "abc"); + i = cmp(r, "bar foo abc"); + assert(i == 0); + + r = format("foo %d", -123); + i = cmp(r, "foo -123"); + assert(i == 0); + + r = format("foo %d", 123); + i = cmp(r, "foo 123"); + assert(i == 0); +} + + +/*********************************************** + * See if character c is in the pattern. + * Patterns: + * + * A pattern is an array of characters much like a character + * class in regular expressions. A sequence of characters + * can be given, such as "abcde". The '-' can represent a range + * of characters, as "a-e" represents the same pattern as "abcde". + * "a-fA-F0-9" represents all the hex characters. + * If the first character of a pattern is '^', then the pattern + * is negated, i.e. "^0-9" means any character except a digit. + * The functions inPattern, countchars, removeschars, + * and squeeze + * use patterns. + * + * Note: In the future, the pattern syntax may be improved + * to be more like regular expression character classes. + */ + +bool inPattern(dchar c, char[] pattern) +{ + bool result = false; + int range = 0; + dchar lastc; + + foreach (size_t i, dchar p; pattern) + { + if (p == '^' && i == 0) + { result = true; + if (i + 1 == pattern.length) + return (c == p); // or should this be an error? + } + else if (range) + { + range = 0; + if (lastc <= c && c <= p || c == p) + return !result; + } + else if (p == '-' && i > result && i + 1 < pattern.length) + { + range = 1; + continue; + } + else if (c == p) + return !result; + lastc = p; + } + return result; +} + + +unittest +{ + debug(string) printf("std.string.inPattern.unittest\n"); + + int i; + + i = inPattern('x', "x"); + assert(i == 1); + i = inPattern('x', "y"); + assert(i == 0); + i = inPattern('x', cast(char[])null); + assert(i == 0); + i = inPattern('x', "^y"); + assert(i == 1); + i = inPattern('x', "yxxy"); + assert(i == 1); + i = inPattern('x', "^yxxy"); + assert(i == 0); + i = inPattern('x', "^abcd"); + assert(i == 1); + i = inPattern('^', "^^"); + assert(i == 0); + i = inPattern('^', "^"); + assert(i == 1); + i = inPattern('^', "a^"); + assert(i == 1); + i = inPattern('x', "a-z"); + assert(i == 1); + i = inPattern('x', "A-Z"); + assert(i == 0); + i = inPattern('x', "^a-z"); + assert(i == 0); + i = inPattern('x', "^A-Z"); + assert(i == 1); + i = inPattern('-', "a-"); + assert(i == 1); + i = inPattern('-', "^A-"); + assert(i == 0); + i = inPattern('a', "z-a"); + assert(i == 1); + i = inPattern('z', "z-a"); + assert(i == 1); + i = inPattern('x', "z-a"); + assert(i == 0); +} + + +/*********************************************** + * See if character c is in the intersection of the patterns. + */ + +int inPattern(dchar c, char[][] patterns) +{ int result; + + foreach (char[] pattern; patterns) + { + if (!inPattern(c, pattern)) + { result = 0; + break; + } + result = 1; + } + return result; +} + + +/******************************************** + * Count characters in s that match pattern. + */ + +size_t countchars(char[] s, char[] pattern) +{ + size_t count; + + foreach (dchar c; s) + { + count += inPattern(c, pattern); + } + return count; +} + + +unittest +{ + debug(string) printf("std.string.count.unittest\n"); + + size_t c; + + c = countchars("abc", "a-c"); + assert(c == 3); + c = countchars("hello world", "or"); + assert(c == 3); +} + + +/******************************************** + * Return string that is s with all characters removed that match pattern. + */ + +char[] removechars(char[] s, char[] pattern) +{ + char[] r = s; + int changed; + size_t j; + + foreach (size_t i, dchar c; s) + { + if (!inPattern(c, pattern)) + { + if (changed) + { + if (r is s) + r = s[0 .. j].dup; + std.utf.encode(r, c); + } + } + else if (!changed) + { changed = 1; + j = i; + } + } + if (changed && r is s) + r = s[0 .. j].dup; + return r; +} + + +unittest +{ + debug(string) printf("std.string.remove.unittest\n"); + + char[] r; + + r = removechars("abc", "a-c"); + assert(r is null); + r = removechars("hello world", "or"); + assert(r == "hell wld"); + r = removechars("hello world", "d"); + assert(r == "hello worl"); +} + + +/*************************************************** + * Return string where sequences of a character in s[] from pattern[] + * are replaced with a single instance of that character. + * If pattern is null, it defaults to all characters. + */ + +char[] squeeze(char[] s, char[] pattern = null) +{ + char[] r = s; + dchar lastc; + size_t lasti; + int run; + bool changed; + + foreach (size_t i, dchar c; s) + { + if (run && lastc == c) + { + changed = true; + } + else if (pattern is null || inPattern(c, pattern)) + { + run = 1; + if (changed) + { if (r is s) + r = s[0 .. lasti].dup; + std.utf.encode(r, c); + } + else + lasti = i + std.utf.stride(s, i); + lastc = c; + } + else + { + run = 0; + if (changed) + { if (r is s) + r = s[0 .. lasti].dup; + std.utf.encode(r, c); + } + } + } + if (changed) + { + if (r is s) + r = s[0 .. lasti]; + } + return r; +} + + +unittest +{ + debug(string) printf("std.string.squeeze.unittest\n"); + char[] s,r; + + r = squeeze("hello"); + //writefln("r = '%s'", r); + assert(r == "helo"); + s = "abcd"; + r = squeeze(s); + assert(r is s); + s = "xyzz"; + r = squeeze(s); + assert(r.ptr == s.ptr); // should just be a slice + r = squeeze("hello goodbyee", "oe"); + assert(r == "hello godbye"); +} + + +/********************************************** + * Return string that is the 'successor' to s[]. + * If the rightmost character is a-zA-Z0-9, it is incremented within + * its case or digits. If it generates a carry, the process is + * repeated with the one to its immediate left. + */ + +char[] succ(char[] s) +{ + if (s.length && isalnum(s[length - 1])) + { + char[] r = s.dup; + size_t i = r.length - 1; + + while (1) + { dchar c = s[i]; + dchar carry; + + switch (c) + { + case '9': + c = '0'; + carry = '1'; + goto Lcarry; + case 'z': + case 'Z': + c -= 'Z' - 'A'; + carry = c; + Lcarry: + r[i] = cast(char)c; + if (i == 0) + { + char[] t = new char[r.length + 1]; + t[0] = cast(char)carry; + t[1 .. length] = r[]; + return t; + } + i--; + break; + + default: + if (std.ctype.isalnum(c)) + r[i]++; + return r; + } + } + } + return s; +} + +unittest +{ + debug(string) printf("std.string.succ.unittest\n"); + + char[] r; + + r = succ(null); + assert(r is null); + r = succ("!@#$%"); + assert(r == "!@#$%"); + r = succ("1"); + assert(r == "2"); + r = succ("9"); + assert(r == "10"); + r = succ("999"); + assert(r == "1000"); + r = succ("zz99"); + assert(r == "aaa00"); +} + + +/*********************************************** + * Replaces characters in str[] that are in from[] + * with corresponding characters in to[] and returns the resulting + * string. + * Params: + * modifiers = a string of modifier characters + * Modifiers: + +
Modifier Description +
c Complement the list of characters in from[] +
d Removes matching characters with no corresponding replacement in to[] +
s Removes adjacent duplicates in the replaced characters +
+ + If modifier d is present, then the number of characters + in to[] may be only 0 or 1. + + If modifier d is not present and to[] is null, + then to[] is taken _to be the same as from[]. + + If modifier d is not present and to[] is shorter + than from[], then to[] is extended by replicating the + last character in to[]. + + Both from[] and to[] may contain ranges using the - + character, for example a-d is synonymous with abcd. + Neither accept a leading ^ as meaning the complement of + the string (use the c modifier for that). + */ + +char[] tr(char[] str, char[] from, char[] to, char[] modifiers = null) +{ + int mod_c; + int mod_d; + int mod_s; + + foreach (char c; modifiers) + { + switch (c) + { + case 'c': mod_c = 1; break; // complement + case 'd': mod_d = 1; break; // delete unreplaced chars + case 's': mod_s = 1; break; // squeeze duplicated replaced chars + default: assert(0); + } + } + + if (to is null && !mod_d) + to = from; + + char[] result = new char[str.length]; + result.length = 0; + int m; + dchar lastc; + + foreach (dchar c; str) + { dchar lastf; + dchar lastt; + dchar newc; + int n = 0; + + for (size_t i = 0; i < from.length; ) + { + dchar f = std.utf.decode(from, i); + //writefln("\tf = '%s', c = '%s', lastf = '%x', '%x', i = %d, %d", f, c, lastf, dchar.init, i, from.length); + if (f == '-' && lastf != dchar.init && i < from.length) + { + dchar nextf = std.utf.decode(from, i); + //writefln("\tlastf = '%s', c = '%s', nextf = '%s'", lastf, c, nextf); + if (lastf <= c && c <= nextf) + { + n += c - lastf - 1; + if (mod_c) + goto Lnotfound; + goto Lfound; + } + n += nextf - lastf; + lastf = lastf.init; + continue; + } + + if (c == f) + { if (mod_c) + goto Lnotfound; + goto Lfound; + } + lastf = f; + n++; + } + if (!mod_c) + goto Lnotfound; + n = 0; // consider it 'found' at position 0 + + Lfound: + + // Find the nth character in to[] + //writefln("\tc = '%s', n = %d", c, n); + dchar nextt; + for (size_t i = 0; i < to.length; ) + { dchar t = std.utf.decode(to, i); + if (t == '-' && lastt != dchar.init && i < to.length) + { + nextt = std.utf.decode(to, i); + //writefln("\tlastt = '%s', c = '%s', nextt = '%s', n = %d", lastt, c, nextt, n); + n -= nextt - lastt; + if (n < 0) + { + newc = nextt + n + 1; + goto Lnewc; + } + lastt = dchar.init; + continue; + } + if (n == 0) + { newc = t; + goto Lnewc; + } + lastt = t; + nextt = t; + n--; + } + if (mod_d) + continue; + newc = nextt; + + Lnewc: + if (mod_s && m && newc == lastc) + continue; + std.utf.encode(result, newc); + m = 1; + lastc = newc; + continue; + + Lnotfound: + std.utf.encode(result, c); + lastc = c; + m = 0; + } + return result; +} + +unittest +{ + debug(string) printf("std.string.tr.unittest\n"); + + char[] r; + //writefln("r = '%s'", r); + + r = tr("abcdef", "cd", "CD"); + assert(r == "abCDef"); + + r = tr("abcdef", "b-d", "B-D"); + assert(r == "aBCDef"); + + r = tr("abcdefgh", "b-dh", "B-Dx"); + assert(r == "aBCDefgx"); + + r = tr("abcdefgh", "b-dh", "B-CDx"); + assert(r == "aBCDefgx"); + + r = tr("abcdefgh", "b-dh", "B-BCDx"); + assert(r == "aBCDefgx"); + + r = tr("abcdef", "ef", "*", "c"); + assert(r == "****ef"); + + r = tr("abcdef", "ef", "", "d"); + assert(r == "abcd"); + + r = tr("hello goodbye", "lo", null, "s"); + assert(r == "helo godbye"); + + r = tr("hello goodbye", "lo", "x", "s"); + assert(r == "hex gxdbye"); + + r = tr("14-Jul-87", "a-zA-Z", " ", "cs"); + assert(r == " Jul "); + + r = tr("Abc", "AAA", "XYZ"); + assert(r == "Xbc"); +} + + +/* ************************************************ + * Version : v0.3 + * Author : David L. 'SpottedTiger' Davis + * Date Created : 31.May.05 Compiled and Tested with dmd v0.125 + * Date Modified : 01.Jun.05 Modified the function to handle the + * : imaginary and complex float-point + * : datatypes. + * : + * Licence : Public Domain / Contributed to Digital Mars + */ + +/** + * [in] char[] s can be formatted in the following ways: + * + * Integer Whole Number: + * (for byte, ubyte, short, ushort, int, uint, long, and ulong) + * ['+'|'-']digit(s)[U|L|UL] + * + * examples: 123, 123UL, 123L, +123U, -123L + * + * Floating-Point Number: + * (for float, double, real, ifloat, idouble, and ireal) + * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] + * or [nan|nani|inf|-inf] + * + * examples: +123., -123.01, 123.3e-10f, 123.3e-10fi, 123.3e-10L + * + * (for cfloat, cdouble, and creal) + * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][+] + * [digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] + * or [nan|nani|nan+nani|inf|-inf] + * + * examples: nan, -123e-1+456.9e-10Li, +123e+10+456i, 123+456 + * + * [in] bool bAllowSep + * False by default, but when set to true it will accept the + * separator characters "," and "_" within the string, but these + * characters should be stripped from the string before using any + * of the conversion functions like toInt(), toFloat(), and etc + * else an error will occur. + * + * Also please note, that no spaces are allowed within the string + * anywhere whether it's a leading, trailing, or embedded space(s), + * thus they too must be stripped from the string before using this + * function, or any of the conversion functions. + */ + +final bool isNumeric(in char[] s, in bool bAllowSep = false) +{ + int iLen = s.length; + bool bDecimalPoint = false; + bool bExponent = false; + bool bComplex = false; + char[] sx = std.string.tolower(s); + int j = 0; + char c; + + //writefln("isNumeric(char[], bool = false) called!"); + // Empty string, return false + if (iLen == 0) + return false; + + // Check for NaN (Not a Number) + if (sx == "nan" || sx == "nani" || sx == "nan+nani") + return true; + + // Check for Infinity + if (sx == "inf" || sx == "-inf") + return true; + + // A sign is allowed only in the 1st character + if (sx[0] == '-' || sx[0] == '+') + j++; + + for (int i = j; i < iLen; i++) + { + c = sx[i]; + + // Digits are good, continue checking + // with the next character... ;) + if (c >= '0' && c <= '9') + continue; + + // Check for the complex type, and if found + // reset the flags for checking the 2nd number. + else if (c == '+') + if (i > 0) + { + bDecimalPoint = false; + bExponent = false; + bComplex = true; + continue; + } + else + return false; + + // Allow only one exponent per number + else if (c == 'e') + { + // A 2nd exponent found, return not a number + if (bExponent) + return false; + + if (i + 1 < iLen) + { + // Look forward for the sign, and if + // missing then this is not a number. + if (sx[i + 1] != '-' && sx[i + 1] != '+') + return false; + else + { + bExponent = true; + i++; + } + } + else + // Ending in "E", return not a number + return false; + } + // Allow only one decimal point per number to be used + else if (c == '.' ) + { + // A 2nd decimal point found, return not a number + if (bDecimalPoint) + return false; + + bDecimalPoint = true; + continue; + } + // Check for ending literal characters: "f,u,l,i,ul,fi,li", + // and wheater they're being used with the correct datatype. + else if (i == iLen - 2) + { + // Integer Whole Number + if (sx[i..iLen] == "ul" && + (!bDecimalPoint && !bExponent && !bComplex)) + return true; + // Floating-Point Number + else if ((sx[i..iLen] == "fi" || sx[i..iLen] == "li") && + (bDecimalPoint || bExponent || bComplex)) + return true; + else if (sx[i..iLen] == "ul" && + (bDecimalPoint || bExponent || bComplex)) + return false; + // Could be a Integer or a Float, thus + // all these suffixes are valid for both + else if (sx[i..iLen] == "ul" || + sx[i..iLen] == "fi" || + sx[i..iLen] == "li") + return true; + else + return false; + } + else if (i == iLen - 1) + { + // Integer Whole Number + if ((c == 'u' || c == 'l') && + (!bDecimalPoint && !bExponent && !bComplex)) + return true; + // Check to see if the last character in the string + // is the required 'i' character + else if (bComplex) + if (c == 'i') + return true; + else + return false; + // Floating-Point Number + else if ((c == 'l' || c == 'f' || c == 'i') && + (bDecimalPoint || bExponent)) + return true; + // Could be a Integer or a Float, thus + // all these suffixes are valid for both + else if (c == 'l' || c == 'f' || c == 'i') + return true; + else + return false; + } + else + // Check if separators are allow + // to be in the numeric string + if (bAllowSep == true && (c == '_' || c == ',')) + continue; + else + return false; + } + + return true; +} + +/// Allow any object as a parameter +bool isNumeric(...) +{ + return isNumeric(_arguments, _argptr); +} + +/// Check only the first parameter, all others will be ignored. +bool isNumeric(TypeInfo[] _arguments, va_list _argptr) +{ + char[] s = ""; + wchar[] ws = ""; + dchar[] ds = ""; + + //writefln("isNumeric(...) called!"); + if (_arguments.length == 0) + return false; + + if (_arguments[0] == typeid(char[])) + return isNumeric(va_arg!(char[])(_argptr)); + else if (_arguments[0] == typeid(wchar[])) + return isNumeric(std.utf.toUTF8(va_arg!(wchar[])(_argptr))); + else if (_arguments[0] == typeid(dchar[])) + return isNumeric(std.utf.toUTF8(va_arg!(dchar[])(_argptr))); + else if (_arguments[0] == typeid(real)) + return true; + else if (_arguments[0] == typeid(double)) + return true; + else if (_arguments[0] == typeid(float)) + return true; + else if (_arguments[0] == typeid(ulong)) + return true; + else if (_arguments[0] == typeid(long)) + return true; + else if (_arguments[0] == typeid(uint)) + return true; + else if (_arguments[0] == typeid(int)) + return true; + else if (_arguments[0] == typeid(ushort)) + return true; + else if (_arguments[0] == typeid(short)) + return true; + else if (_arguments[0] == typeid(ubyte)) + { + s.length = 1; + s[0]= va_arg!(ubyte)(_argptr); + return isNumeric(cast(char[])s); + } + else if (_arguments[0] == typeid(byte)) + { + s.length = 1; + s[0] = va_arg!(byte)(_argptr); + return isNumeric(cast(char[])s); + } + else if (_arguments[0] == typeid(ireal)) + return true; + else if (_arguments[0] == typeid(idouble)) + return true; + else if (_arguments[0] == typeid(ifloat)) + return true; + else if (_arguments[0] == typeid(creal)) + return true; + else if (_arguments[0] == typeid(cdouble)) + return true; + else if (_arguments[0] == typeid(cfloat)) + return true; + else if (_arguments[0] == typeid(char)) + { + s.length = 1; + s[0] = va_arg!(char)(_argptr); + return isNumeric(s); + } + else if (_arguments[0] == typeid(wchar)) + { + ws.length = 1; + ws[0] = va_arg!(wchar)(_argptr); + return isNumeric(std.utf.toUTF8(ws)); + } + else if (_arguments[0] == typeid(dchar)) + { + ds.length = 1; + ds[0] = va_arg!(dchar)(_argptr); + return isNumeric(std.utf.toUTF8(ds)); + } + //else if (_arguments[0] == typeid(cent)) + // return true; + //else if (_arguments[0] == typeid(ucent)) + // return true; + else + return false; +} + +unittest +{ + debug (string) printf("isNumeric(in char[], bool = false).unittest\n"); + char[] s; + + // Test the isNumeric(in char[]) function + assert(isNumeric("1") == true ); + assert(isNumeric("1.0") == true ); + assert(isNumeric("1e-1") == true ); + assert(isNumeric("12345xxxx890") == false ); + assert(isNumeric("567L") == true ); + assert(isNumeric("23UL") == true ); + assert(isNumeric("-123..56f") == false ); + assert(isNumeric("12.3.5.6") == false ); + assert(isNumeric(" 12.356") == false ); + assert(isNumeric("123 5.6") == false ); + assert(isNumeric("1233E-1+1.0e-1i") == true ); + + assert(isNumeric("123.00E-5+1234.45E-12Li") == true); + assert(isNumeric("123.00e-5+1234.45E-12iL") == false); + assert(isNumeric("123.00e-5+1234.45e-12uL") == false); + assert(isNumeric("123.00E-5+1234.45e-12lu") == false); + + assert(isNumeric("123fi") == true); + assert(isNumeric("123li") == true); + assert(isNumeric("--123L") == false); + assert(isNumeric("+123.5UL") == false); + assert(isNumeric("123f") == true); + assert(isNumeric("123.u") == false); + + assert(isNumeric(std.string.toString(real.nan)) == true); + assert(isNumeric(std.string.toString(-real.infinity)) == true); + assert(isNumeric(std.string.toString(123e+2+1234.78Li)) == true); + + s = "$250.99-"; + assert(isNumeric(s[1..s.length - 2]) == true); + assert(isNumeric(s) == false); + assert(isNumeric(s[0..s.length - 1]) == false); + + // These test calling the isNumeric(...) function + assert(isNumeric(1,123UL) == true); + assert(isNumeric('2') == true); + assert(isNumeric('x') == false); + assert(isNumeric(cast(byte)0x57) == false); // 'W' + assert(isNumeric(cast(byte)0x37) == true); // '7' + assert(isNumeric(cast(wchar[])"145.67") == true); + assert(isNumeric(cast(dchar[])"145.67U") == false); + assert(isNumeric(123_000.23fi) == true); + assert(isNumeric(123.00E-5+1234.45E-12Li) == true); + assert(isNumeric(real.nan) == true); + assert(isNumeric(-real.infinity) == true); +} + + +/***************************** + * Soundex algorithm. + * + * The Soundex algorithm converts a word into 4 characters + * based on how the word sounds phonetically. The idea is that + * two spellings that sound alike will have the same Soundex + * value, which means that Soundex can be used for fuzzy matching + * of names. + * + * Params: + * string = String to convert to Soundex representation. + * buffer = Optional 4 char array to put the resulting Soundex + * characters into. If null, the return value + * buffer will be allocated on the heap. + * Returns: + * The four character array with the Soundex result in it. + * Returns null if there is no Soundex representation for the string. + * + * See_Also: + * $(LINK2 http://en.wikipedia.org/wiki/Soundex, Wikipedia), + * $(LINK2 http://www.archives.gov/publications/general-info-leaflets/55.html, The Soundex Indexing System) + * + * Bugs: + * Only works well with English names. + * There are other arguably better Soundex algorithms, + * but this one is the standard one. + */ + +char[] soundex(char[] string, char[] buffer = null) +in +{ + assert(!buffer || buffer.length >= 4); +} +out (result) +{ + if (result) + { + assert(result.length == 4); + assert(result[0] >= 'A' && result[0] <= 'Z'); + foreach (char c; result[1 .. 4]) + assert(c >= '0' && c <= '6'); + } +} +body +{ + static char[26] dex = + // ABCDEFGHIJKLMNOPQRSTUVWXYZ + "01230120022455012623010202"; + + int b = 0; + char lastc; + foreach (char c; string) + { + if (c >= 'a' && c <= 'z') + c -= 'a' - 'A'; + else if (c >= 'A' && c <= 'Z') + { + ; + } + else + { lastc = lastc.init; + continue; + } + if (b == 0) + { + if (!buffer) + buffer = new char[4]; + buffer[0] = c; + b++; + lastc = dex[c - 'A']; + } + else + { + if (c == 'H' || c == 'W') + continue; + if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') + lastc = lastc.init; + c = dex[c - 'A']; + if (c != '0' && c != lastc) + { + buffer[b] = c; + b++; + lastc = c; + } + } + if (b == 4) + goto Lret; + } + if (b == 0) + buffer = null; + else + buffer[b .. 4] = '0'; +Lret: + return buffer; +} + +unittest +{ char[4] buffer; + + assert(soundex(null) == null); + assert(soundex("") == null); + assert(soundex("0123^&^^**&^") == null); + assert(soundex("Euler") == "E460"); + assert(soundex(" Ellery ") == "E460"); + assert(soundex("Gauss") == "G200"); + assert(soundex("Ghosh") == "G200"); + assert(soundex("Hilbert") == "H416"); + assert(soundex("Heilbronn") == "H416"); + assert(soundex("Knuth") == "K530"); + assert(soundex("Kant", buffer) == "K530"); + assert(soundex("Lloyd") == "L300"); + assert(soundex("Ladd") == "L300"); + assert(soundex("Lukasiewicz", buffer) == "L222"); + assert(soundex("Lissajous") == "L222"); + assert(soundex("Robert") == "R163"); + assert(soundex("Rupert") == "R163"); + assert(soundex("Rubin") == "R150"); + assert(soundex("Washington") == "W252"); + assert(soundex("Lee") == "L000"); + assert(soundex("Gutierrez") == "G362"); + assert(soundex("Pfister") == "P236"); + assert(soundex("Jackson") == "J250"); + assert(soundex("Tymczak") == "T522"); + assert(soundex("Ashcraft") == "A261"); + + assert(soundex("Woo") == "W000"); + assert(soundex("Pilgrim") == "P426"); + assert(soundex("Flingjingwaller") == "F452"); + assert(soundex("PEARSE") == "P620"); + assert(soundex("PIERCE") == "P620"); + assert(soundex("Price") == "P620"); + assert(soundex("CATHY") == "C300"); + assert(soundex("KATHY") == "K300"); + assert(soundex("Jones") == "J520"); + assert(soundex("johnsons") == "J525"); + assert(soundex("Hardin") == "H635"); + assert(soundex("Martinez") == "M635"); +} + + +/*************************************************** + * Construct an associative array consisting of all + * abbreviations that uniquely map to the strings in values. + * + * This is useful in cases where the user is expected to type + * in one of a known set of strings, and the program will helpfully + * autocomplete the string once sufficient characters have been + * entered that uniquely identify it. + * Example: + * --- + * import std.stdio; + * import std.string; + * + * void main() + * { + * static char[][] list = [ "food", "foxy" ]; + * + * auto abbrevs = std.string.abbrev(list); + * + * foreach (key, value; abbrevs) + * { + * writefln("%s => %s", key, value); + * } + * } + * --- + * produces the output: + *
+ * fox => foxy
+ * food => food
+ * foxy => foxy
+ * foo => food
+ * 
+ */ + +char[][char[]] abbrev(char[][] values) +{ + char[][char[]] result; + + // Make a copy when sorting so we follow COW principles. + values = values.dup.sort; + + size_t values_length = values.length; + size_t lasti = values_length; + size_t nexti; + + char[] nv; + char[] lv; + + for (size_t i = 0; i < values_length; i = nexti) + { char[] value = values[i]; + + // Skip dups + for (nexti = i + 1; nexti < values_length; nexti++) + { nv = values[nexti]; + if (value != values[nexti]) + break; + } + + for (size_t j = 0; j < value.length; j += std.utf.stride(value, j)) + { char[] v = value[0 .. j]; + + if ((nexti == values_length || j > nv.length || v != nv[0 .. j]) && + (lasti == values_length || j > lv.length || v != lv[0 .. j])) + result[v] = value; + } + result[value] = value; + lasti = i; + lv = value; + } + + return result; +} + +unittest +{ + debug(string) printf("string.abbrev.unittest\n"); + + char[][] values; + values ~= "hello"; + values ~= "hello"; + values ~= "he"; + + char[][char[]] r; + + r = abbrev(values); + char[][] keys = r.keys.dup; + keys.sort; + + assert(keys.length == 4); + assert(keys[0] == "he"); + assert(keys[1] == "hel"); + assert(keys[2] == "hell"); + assert(keys[3] == "hello"); + + assert(r[keys[0]] == "he"); + assert(r[keys[1]] == "hello"); + assert(r[keys[2]] == "hello"); + assert(r[keys[3]] == "hello"); +} + + +/****************************************** + * Compute column number after string if string starts in the + * leftmost column, which is numbered starting from 0. + */ + +size_t column(char[] string, int tabsize = 8) +{ + size_t column; + + foreach (dchar c; string) + { + switch (c) + { + case '\t': + column = (column + tabsize) / tabsize * tabsize; + break; + + case '\r': + case '\n': + case PS: + case LS: + column = 0; + break; + + default: + column++; + break; + } + } + return column; +} + +unittest +{ + debug(string) printf("string.column.unittest\n"); + + assert(column(null) == 0); + assert(column("") == 0); + assert(column("\t") == 8); + assert(column("abc\t") == 8); + assert(column("12345678\t") == 16); +} + +/****************************************** + * Wrap text into a paragraph. + * + * The input text string s is formed into a paragraph + * by breaking it up into a sequence of lines, delineated + * by \n, such that the number of columns is not exceeded + * on each line. + * The last line is terminated with a \n. + * Params: + * s = text string to be wrapped + * columns = maximum number of _columns in the paragraph + * firstindent = string used to _indent first line of the paragraph + * indent = string to use to _indent following lines of the paragraph + * tabsize = column spacing of tabs + * Returns: + * The resulting paragraph. + */ + +char[] wrap(char[] s, int columns = 80, char[] firstindent = null, + char[] indent = null, int tabsize = 8) +{ + char[] result; + int col; + int spaces; + bool inword; + bool first = true; + size_t wordstart; + + result.length = firstindent.length + s.length; + result.length = firstindent.length; + result[] = firstindent[]; + col = column(result, tabsize); + foreach (size_t i, dchar c; s) + { + if (iswhite(c)) + { + if (inword) + { + if (first) + { + ; + } + else if (col + 1 + (i - wordstart) > columns) + { + result ~= '\n'; + result ~= indent; + col = column(indent, tabsize); + } + else + { result ~= ' '; + col += 1; + } + result ~= s[wordstart .. i]; + col += i - wordstart; + inword = false; + first = false; + } + } + else + { + if (!inword) + { + wordstart = i; + inword = true; + } + } + } + + if (inword) + { + if (col + 1 + (s.length - wordstart) >= columns) + { + result ~= '\n'; + result ~= indent; + } + else if (result.length != firstindent.length) + result ~= ' '; + result ~= s[wordstart .. s.length]; + } + result ~= '\n'; + + return result; +} + +unittest +{ + debug(string) printf("string.wrap.unittest\n"); + + assert(wrap(null) == "\n"); + assert(wrap(" a b df ") == "a b df\n"); + //writefln("'%s'", wrap(" a b df ",3)); + assert(wrap(" a b df ", 3) == "a b\ndf\n"); + assert(wrap(" a bc df ", 3) == "a\nbc\ndf\n"); + //writefln("'%s'", wrap(" abcd df ",3)); + assert(wrap(" abcd df ", 3) == "abcd\ndf\n"); + assert(wrap("x") == "x\n"); + assert(wrap("u u") == "u u\n"); +} + + +/*************************** + * Does string s[] start with an email address? + * Returns: + * null it does not + * char[] it does, and this is the slice of s[] that is that email address + * References: + * RFC2822 + */ +char[] isEmail(char[] s) +{ size_t i; + + if (!isalpha(s[0])) + goto Lno; + + for (i = 1; 1; i++) + { + if (i == s.length) + goto Lno; + auto c = s[i]; + if (isalnum(c)) + continue; + if (c == '-' || c == '_' || c == '.') + continue; + if (c != '@') + goto Lno; + i++; + break; + } + //writefln("test1 '%s'", s[0 .. i]); + + /* Now do the part past the '@' + */ + size_t lastdot; + for (; i < s.length; i++) + { + auto c = s[i]; + if (isalnum(c)) + continue; + if (c == '-' || c == '_') + continue; + if (c == '.') + { + lastdot = i; + continue; + } + break; + } + if (!lastdot || (i - lastdot != 3 && i - lastdot != 4)) + goto Lno; + + return s[0 .. i]; + +Lno: + return null; +} + + +/*************************** + * Does string s[] start with a URL? + * Returns: + * null it does not + * char[] it does, and this is the slice of s[] that is that URL + */ + +char[] isURL(char[] s) +{ + /* Must start with one of: + * http:// + * https:// + * www. + */ + + size_t i; + + if (s.length <= 4) + goto Lno; + + //writefln("isURL(%s)", s); + if (s.length > 7 && std.string.icmp(s[0 .. 7], "http://") == 0) + i = 7; + else if (s.length > 8 && std.string.icmp(s[0 .. 8], "https://") == 0) + i = 8; +// if (icmp(s[0 .. 4], "www.") == 0) +// i = 4; + else + goto Lno; + + size_t lastdot; + for (; i < s.length; i++) + { + auto c = s[i]; + if (isalnum(c)) + continue; + if (c == '-' || c == '_' || c == '?' || + c == '=' || c == '%' || c == '&' || + c == '/' || c == '+' || c == '#' || + c == '~') + continue; + if (c == '.') + { + lastdot = i; + continue; + } + break; + } + //if (!lastdot || (i - lastdot != 3 && i - lastdot != 4)) + if (!lastdot) + goto Lno; + + return s[0 .. i]; + +Lno: + return null; +} + + diff -r 3efbcc81ba45 -r 288fe1029e1f lphobos/typeinfos2.d --- a/lphobos/typeinfos2.d Tue Nov 20 00:02:35 2007 +0100 +++ b/lphobos/typeinfos2.d Tue Nov 20 05:29:20 2007 +0100 @@ -1,7 +1,7 @@ module typeinfos2; import -//typeinfo2.to_AC, +typeinfo2.ti_AC, typeinfo2.ti_Adouble, typeinfo2.ti_Afloat, typeinfo2.ti_Ag, diff -r 3efbcc81ba45 -r 288fe1029e1f test/aa1.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/aa1.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,6 @@ +module aa1; + +void main() +{ + int[int] aai; +} diff -r 3efbcc81ba45 -r 288fe1029e1f test/bug70.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug70.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,9 @@ +module bug70; + +void main() +{ + for (;false;) + { + break; + } +} diff -r 3efbcc81ba45 -r 288fe1029e1f test/bug71.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug71.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,20 @@ +module bug71; + +void main() +{ + static TypeInfo skipCI(TypeInfo valti) + { + while (1) + { + if (valti.classinfo.name.length == 18 && + valti.classinfo.name[9..18] == "Invariant") + valti = (cast(TypeInfo_Invariant)valti).next; + else if (valti.classinfo.name.length == 14 && + valti.classinfo.name[9..14] == "Const") + valti = (cast(TypeInfo_Const)valti).next; + else + break; + } + return valti; + } +} \ No newline at end of file diff -r 3efbcc81ba45 -r 288fe1029e1f test/bug72.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug72.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,8 @@ +module bug72; + +void main() +{ + char c = void; + int i = void; + c += i; +} diff -r 3efbcc81ba45 -r 288fe1029e1f test/bug73.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug73.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,14 @@ +int find(char[] s, dchar c) +{ + // c is a universal character + foreach (int i, dchar c2; s) + { + if (c == c2) + return i; + } + return -1; +} + +void main() +{ +} \ No newline at end of file diff -r 3efbcc81ba45 -r 288fe1029e1f test/switch2.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/switch2.d Tue Nov 20 05:29:20 2007 +0100 @@ -0,0 +1,12 @@ +module switch2; + +void main() +{ + int i = 2; + switch(i) + { + case 0: assert(0); + case 1,2: printf("hello world\n"); break; + default: assert(0); + } +}