PageRenderTime 170ms CodeModel.GetById 40ms app.highlight 63ms RepoModel.GetById 61ms app.codeStats 0ms

/core/string.d

http://github.com/wilkie/djehuty
D | 1140 lines | 906 code | 189 blank | 45 comment | 278 complexity | 2eb43493567fa1bbb9a0d1e00481290c MD5 | raw file
   1/*
   2 * string.d
   3 *
   4 * This file contains the native String class. A string class that wraps a
   5 * string that is represented in the native preferred unicode class.
   6 *
   7 * Author: Dave Wilkinson
   8 *
   9 */
  10
  11module core.string;
  12
  13// a class to encapsulate the expected string format for
  14// the underlying OS platform in use
  15
  16import core.definitions;
  17import core.unicode;
  18import core.variant;
  19
  20import data.iterable;
  21
  22import io.console;
  23
  24import math.currency;
  25
  26public import core.string;
  27
  28string toStrv(Variadic vars) {
  29	string ret = "";
  30	foreach(var; vars) {
  31		ret ~= var.toString();
  32	}
  33	return ret;
  34}
  35
  36string toStr(...) {
  37	Variadic vars = new Variadic(_arguments, _argptr);
  38
  39	return toStrv(vars);
  40}
  41
  42// Standard string functions (for C)
  43
  44// strlen
  45size_t strlen(char* chr) {
  46	size_t ret = 0;
  47	while(*chr++) {
  48		ret++;
  49	}
  50	return ret;
  51}
  52
  53size_t strlen(wchar* chr) {
  54	size_t ret = 0;
  55	while(*chr++) {
  56		ret++;
  57	}
  58	return ret;
  59}
  60
  61void strncpy(char* dest, char* src, int length) {
  62	while(((*dest++ = *src++) != 0) && --length){}
  63	*(--dest) = '\0';
  64}
  65
  66void strncat(char* dest, char* src, int length) {
  67	while(*dest++){}
  68	strncpy(--dest,src,length);
  69}
  70
  71int strcmp(char* a, char* b) {
  72	while((*a == *b) && *a++ && *b++){}
  73	return *a - *b;
  74}
  75
  76int strncmp(char* a, char* b, int length) {
  77	while(--length && (*a == *b) && *a++ && *b++){}
  78	return *a - *b;
  79}
  80
  81void strcpy(char* dest, char* src) {
  82	while((*dest++ = *src++) != 0){}
  83}
  84
  85void strcat(char* dest, char* src) {
  86	while(*dest++){}
  87	strcpy(--dest,src);
  88}
  89
  90void strupr(char* str) {
  91	while(*str) {
  92		if (*str >= 'a' || *str <= 'z') {
  93			*str -= 32;
  94		}
  95		str++;
  96	}
  97}
  98
  99void strlwr(char* str) {
 100    while(*str) {
 101		if (*str >= 'A' || *str <= 'Z') {
 102			*str += 32;
 103		}
 104		str++;
 105	}
 106}
 107
 108string trim(string chrs) {
 109	size_t idx_s, idx_e;
 110
 111	idx_e = chrs.length;
 112
 113	while (idx_s < chrs.length && (chrs[idx_s] == ' ' || chrs[idx_s] == '\t' || chrs[idx_s] == '\n' || chrs[idx_s] == '\r')) {
 114		idx_s++;
 115	}
 116
 117	while (idx_e > 0 && (chrs[idx_e-1] == ' ' || chrs[idx_e-1] == '\t' || chrs[idx_e-1] == '\n' || chrs[idx_e-1] == '\r')) {
 118		idx_e--;
 119	}
 120
 121	if (idx_s >= idx_e) {
 122		return "";
 123	}
 124
 125	return chrs[idx_s..idx_e].dup;
 126}
 127
 128string[] split(string input, string delims) {
 129	string[] retstring;
 130	size_t last;
 131
 132	foreach(size_t i, char c; input) {
 133		foreach(char d; delims) {
 134			if (c == d) {
 135				retstring ~= input[last..i];
 136				last = i+1;
 137			}
 138		}
 139	}
 140	
 141	retstring ~= input[last..$];
 142
 143	return retstring;
 144}
 145
 146string[] split(string input, char delim) {
 147	string[] retstring;
 148	size_t last;
 149
 150	foreach(size_t i, char c; input) {
 151		if (c == delim) {
 152			retstring ~= input[last..i];
 153			last = i+1;
 154		}
 155	}
 156
 157	retstring ~= input[last..$];
 158
 159	return retstring;
 160}
 161
 162template _nextInt(T) {
 163	bool _nextInt(T)(string str, out T value) {
 164		int curpos;
 165		if (str.length == 0) {
 166			return false;
 167		}
 168
 169		for(curpos=0; curpos<str.length; curpos++) {
 170			if (str[curpos] != ' ' &&
 171					str[curpos] != '\t' &&
 172					str[curpos] != '\r' &&
 173					str[curpos] != '\n') {
 174
 175				break;
 176			}
 177		}
 178
 179		bool negative = false;
 180
 181		if (str[curpos] == '-') {
 182			negative = true;
 183			curpos++;
 184			if (curpos == str.length) { return false; }
 185		}
 186
 187		if (str[curpos] < '0' ||
 188				str[curpos] > '9') {
 189
 190			return false;
 191		}
 192
 193		long tmpval = 0;
 194
 195		for (;curpos<str.length;curpos++) {
 196			if (str[curpos] < '0' ||
 197					str[curpos] > '9') {
 198
 199				break;
 200			}
 201
 202			tmpval *= 10;
 203			tmpval += cast(long)(str[curpos] - '0');
 204		}
 205
 206		if (negative) { tmpval = -tmpval; }
 207
 208		value = cast(T)tmpval;
 209
 210		return true;
 211	}
 212}
 213
 214// Description: This function will return the next integer value found in the string.
 215bool nextInt(string str, out int value) {
 216	return _nextInt!(int)(str, value);
 217}
 218
 219bool nextInt(string str, out uint value) {
 220	return _nextInt!(uint)(str, value);
 221}
 222
 223bool nextInt(string str, out long value) {
 224	return _nextInt!(long)(str, value);
 225}
 226
 227bool nextInt(string str, out ulong value) {
 228	return _nextInt!(ulong)(str, value);
 229}
 230
 231bool nextInt(string str, out short value) {
 232	return _nextInt!(short)(str, value);
 233}
 234
 235bool nextInt(string str, out ushort value) {
 236	return _nextInt!(ushort)(str, value);
 237}
 238
 239// Description: Will build and return a String object representing a slice of the current String.
 240// start: The position to start from.
 241// len: The length of the slice.  Pass -1 to get the remaining string.
 242string substring(string str, int start, int len = -1) {
 243	uint[] _indices = Unicode.calcIndices(str);
 244
 245	if (start >= _indices.length || len == 0) {
 246		return "";
 247	}
 248
 249	if (len < 0) { len = -1; }
 250
 251	if (len >= 0 && start + len >= _indices.length) {
 252		len = -1;
 253	}
 254
 255	// subdivide
 256
 257	if (len == -1) {
 258		start = _indices[start];
 259		string ret = "";
 260		ret = str[start..$].dup;
 261		return ret;
 262	}
 263
 264	// this is the index for one character past the
 265	// end of the substring of the original string...hence, len is
 266	// now the exclusive end of the range to slice the array.
 267	len = _indices[start+len];
 268
 269	start = _indices[start];
 270
 271	string ret = "";
 272	ret = str[start..len].dup;
 273	return ret;
 274}
 275
 276string replace(string str, dchar find, dchar replace) {
 277	string ret = str.dup;
 278	uint[] _indices = Unicode.calcIndices(str);
 279
 280	for(int i = 0; i < _indices.length; i++) {
 281		dchar cmpChar = Unicode.toUtf32Char(ret[_indices[i]..$]);
 282		if (cmpChar == find) {
 283			dchar[1] chrs = [replace];
 284			ret = ret[0.._indices[i]] ~ Unicode.toUtf8(chrs) ~ ret[_indices[i+1]..$];
 285			_indices = Unicode.calcIndices(ret);
 286		}
 287	}
 288
 289	return ret;
 290}
 291
 292int findReverse(string source, string search, uint start = uint.max) {
 293	if (start == uint.max) {
 294		start = source.length;
 295	}
 296
 297	if (search == "") {
 298		return -1;
 299	}
 300
 301	uint[] _indices = Unicode.calcIndices(source);
 302	uint[] search_indices = Unicode.calcIndices(search);
 303
 304	bool found;
 305
 306	int o;
 307	foreach_reverse(i, aPos; _indices[0..start]) {
 308		found = true;
 309		o=i-1;
 310		foreach(bPos; search_indices) {
 311			o++;
 312			if(o >= _indices.length) {
 313				found = false;
 314				break;
 315			}
 316
 317			dchar aChr, bChr;
 318
 319			aChr = Unicode.toUtf32Char(source[_indices[o]..$]);
 320			bChr = Unicode.toUtf32Char(search[bPos..$]);
 321
 322			if(aChr != bChr) {
 323				found = false;
 324				break;
 325			}
 326		}
 327		if (found) {
 328			return i;
 329		}
 330	}
 331
 332	return -1;
 333}
 334
 335int find(string source, string search, uint start = 0) {
 336	// look through string for term search
 337	// in some, hopefully later on, efficient manner
 338
 339	uint[] _indices = Unicode.calcIndices(source);
 340	uint[] search_indices = Unicode.calcIndices(search);
 341
 342	if (search == "") {
 343		return -1;
 344	}
 345
 346	if (start >= _indices.length) {
 347		return -1;
 348	}
 349
 350	bool found;
 351
 352	int o;
 353
 354	foreach (i, aPos; _indices[start..$]) {
 355		found = true;
 356		o=i-1+start;
 357		foreach (bPos; search_indices) {
 358			o++;
 359			if (o >= _indices.length) {
 360				found = false;
 361				break;
 362			}
 363
 364			dchar aChr, bChr;
 365
 366			aChr = Unicode.toUtf32Char(source[_indices[o]..$]);
 367			bChr = Unicode.toUtf32Char(search[bPos..$]);
 368
 369			if (aChr != bChr) {
 370				found = false;
 371				break;
 372			}
 373		}
 374		if (found) {
 375			return i+start;
 376		}
 377	}
 378
 379	return -1;
 380}
 381
 382string times(string str, uint amount) {
 383	if (amount == 0) {
 384		return "";
 385	}
 386
 387	string ret = "";
 388	for(int i = 0; i < amount; i++) {
 389		ret ~= str;
 390	}
 391	return ret;
 392}
 393
 394string format(string format, ...) {
 395	Variadic vars = new Variadic(_arguments, _argptr);
 396	return formatv(format, vars);
 397}
 398
 399string formatv(string format, Variadic vars) {
 400	string ret = "";
 401	string specifier = "";
 402	bool inFormat = false;
 403	bool intoFormat = false;
 404	foreach(chr; format) {
 405		if (intoFormat && chr != '{') {
 406			intoFormat = false;
 407			inFormat = true;
 408			specifier = "";
 409		}
 410
 411		if (inFormat) {
 412			// look for format end
 413			if (chr == '}') {
 414				inFormat = false;
 415
 416				int index = 0;
 417				int width = 0;
 418				int precision = 0;
 419				int base = 10;
 420				bool unsigned = false;
 421				long value;
 422				ulong uvalue;
 423				float fvalue;
 424				double dvalue;
 425
 426				bool formatIndex = false;
 427				bool formatNumber = false;
 428				bool formatFloat = false;
 429				bool formatDouble = false;
 430				bool formatUpper = false;
 431				bool formatCurrency = false;
 432
 433				if (specifier.nextInt(index)) {
 434					string newSpecifier = specifier.substring(toStr(index).length);
 435					if (newSpecifier.length > 0 && newSpecifier[0] != ':') {
 436						// Not an index
 437					}
 438					else {
 439						specifier = newSpecifier;
 440						formatIndex = true;
 441					}
 442				}
 443
 444				// interpret format specifier
 445				if (specifier.length > 0) {
 446					if (specifier[0] == ':') {
 447						specifier = specifier[1..$];
 448					}
 449				}
 450				if (specifier.length == 0) {
 451					specifier = ":";
 452				}
 453
 454				switch(specifier[0]) {
 455					case 'd':
 456					case 'D':	// Decimal
 457						base = 10;
 458						formatNumber = true;
 459						specifier[1..$].nextInt(width);
 460						break;
 461					case 'x':
 462					case 'X':	// Hexidecimal
 463						base = 16;
 464						unsigned = true;
 465						formatNumber = true;
 466						specifier[1..$].nextInt(width);
 467						break;
 468					case 'o':
 469					case 'O':	// Octal
 470						base = 8;
 471						unsigned = true;
 472						formatNumber = true;
 473						specifier[1..$].nextInt(width);
 474						break;
 475					case 'u':
 476					case 'U':	// Unsigned Integer
 477						base = 10;
 478						unsigned = true;
 479						formatNumber = true;
 480						break;
 481					case 'c':	// Currency
 482						base = 10;
 483						unsigned = false;
 484						formatNumber = true;
 485						formatCurrency = true;
 486						break;
 487					default:
 488						// Other specifier series
 489						// Parse them
 490
 491						width = 0;
 492						precision = 0;
 493						bool gettingPrecision = false;
 494						foreach(c; specifier) {
 495
 496							// Zero Placeholders
 497							if (c == '0') {
 498								if (gettingPrecision) {
 499									precision++;
 500								}
 501								else {
 502									width++;
 503								}
 504							}
 505							else if (c == '.') {
 506								if (gettingPrecision) {
 507									throw new Exception("Format Exception");
 508								}
 509								gettingPrecision = true;
 510							}
 511						}
 512						if (width > 0 || precision > 0) {
 513							formatNumber = true;
 514						}
 515						break;
 516				}
 517
 518				specifier = specifier[0..1];
 519
 520				// Pull an argument off of the stack
 521				Variant var;
 522				if (formatIndex) {
 523					if (index < vars.length) {
 524						var = vars[index];
 525					}
 526					else {
 527						// TODO: More descriptive and uniform exception?
 528						throw new Exception("Invalid Format String");
 529					}
 530				}
 531				else {
 532					var = vars.next();
 533				}
 534
 535				Type type = var.type;
 536				if (formatNumber) {
 537					switch (type) {
 538						case Type.Byte:
 539						case Type.Ubyte:
 540						case Type.Short:
 541						case Type.Ushort:
 542						case Type.Int:
 543						case Type.Uint:
 544						case Type.Long:
 545							value = var.to!(long);
 546							uvalue = var.to!(ulong);
 547							break;
 548						case Type.Ulong:
 549							unsigned = true;
 550							uvalue = var.to!(ulong);
 551							break;
 552						case Type.Float:
 553							fvalue = var.to!(float);
 554							formatFloat = true;
 555							break;
 556						case Type.Double:
 557							dvalue = var.to!(double);
 558							formatDouble = true;
 559							break;
 560						default:
 561							break;
 562					}
 563
 564					string result = "";
 565					if (formatCurrency) {
 566						if (formatFloat) {
 567							result ~= (new Currency(fvalue)).toString();
 568						}
 569						else if (formatDouble) {
 570							result ~= (new Currency(dvalue)).toString();
 571						}
 572						else {
 573							result ~= var.toString();
 574						}
 575					}
 576					else if (formatFloat || formatDouble) {
 577						string[] foo;
 578						if (formatFloat) {
 579							foo = pftoa(fvalue, base);
 580						}
 581						else {
 582							foo = pdtoa(dvalue, base);
 583						}
 584						result = foo[0];
 585						while (result.length < width) {
 586							result = "0" ~ result;
 587						}
 588						string dec = foo[1];
 589						while (dec.length < precision) {
 590							dec ~= "0";
 591						}
 592						result ~= "." ~ dec;
 593					}
 594					else if (formatDouble) {
 595						result = dtoa(dvalue, base);
 596					}
 597					else {
 598						if (unsigned) {
 599							result = utoa(uvalue, base);
 600						}
 601						else {
 602							result = itoa(value, base);
 603						}
 604
 605						while (result.length < width) {
 606							result = "0" ~ result;
 607						}
 608					}
 609					if (specifier.uppercase() == specifier) {
 610						result = result.uppercase();
 611					}
 612					ret ~= result;
 613				}
 614				else {
 615					ret ~= var.toString();
 616				}
 617			}
 618			specifier ~= chr;
 619		}
 620		else {
 621			// Go into a format specifier
 622			if (chr == '{' && intoFormat == false) {
 623				intoFormat = true;
 624				continue;
 625			}
 626			intoFormat = false;
 627			ret ~= chr;
 628		}
 629	}
 630	return ret;
 631}
 632
 633string lowercase(string str) {
 634	string ret = str.dup; 
 635	foreach(ref chr; ret) {
 636		if (chr >= 'A' && chr <= 'Z') {
 637			chr = cast(char)((cast(byte)chr) + 32);
 638		}
 639	}
 640	return ret;
 641}
 642
 643string uppercase(string str) {
 644	string ret = str.dup; 
 645	foreach(ref chr; ret) {
 646		if (chr >= 'a' && chr <= 'z') {
 647			chr = cast(char)((cast(byte)chr) - 32);
 648		}
 649	}
 650	return ret;
 651}
 652
 653string charAt(string str, uint idx) {
 654	uint[] _indices = Unicode.calcIndices(str);
 655	int start, end;
 656	if (_indices.length > idx) {
 657		start = _indices[idx];
 658	}
 659	else {
 660		return null;
 661	}
 662	if (_indices.length > idx+1) {
 663		end = _indices[idx+1];
 664	}
 665	else {
 666		end = str.length;
 667	}
 668	return str[start..end];
 669}
 670
 671string insertAt(string str, string what, uint idx) {
 672	uint[] _indices = Unicode.calcIndices(str);
 673	int pos;
 674	if (_indices.length > idx) {
 675		pos = _indices[idx];
 676	}
 677	else if (idx == _indices.length) {
 678		pos = str.length;
 679	}
 680	else {
 681		return null;
 682	}
 683
 684	return str[0..pos] ~ what ~ str[pos..$];
 685}
 686
 687int utflen(string str) {
 688	uint[] _indices = Unicode.calcIndices(str);
 689	return _indices.length;
 690}
 691
 692int toInt(string str) {
 693	int ret;
 694	if (str.nextInt(ret)) {
 695		return ret;
 696	}
 697	return 0;
 698}
 699
 700private:
 701string itoa(long val, uint base = 10) {
 702	int intlen;
 703	long tmp = val;
 704
 705    bool negative;
 706
 707    if (tmp < 0) {
 708        negative = true;
 709        tmp = -tmp;
 710        intlen = 2;
 711    }
 712    else {
 713        negative = false;
 714        intlen = 1;
 715    }
 716
 717    while (tmp >= base) {
 718        tmp /= base;
 719        intlen++;
 720    }
 721
 722    //allocate
 723
 724    string ret = new char[intlen];
 725
 726    intlen--;
 727
 728    if (negative) {
 729        tmp = -val;
 730    } else {
 731        tmp = val;
 732    }
 733
 734    do {
 735    	uint off = cast(uint)(tmp % base);
 736    	char replace;
 737    	if (off < 10) {
 738    		replace = cast(char)('0' + off);
 739    	}
 740    	else if (off < 36) {
 741    		off -= 10;
 742    		replace = cast(char)('a' + off);
 743    	}
 744        ret[intlen] = replace;
 745        tmp /= base;
 746        intlen--;
 747    } while (tmp != 0);
 748
 749
 750    if (negative) {
 751        ret[intlen] = '-';
 752    }
 753
 754    return ret;
 755}
 756
 757string utoa(ulong val, uint base = 10) {
 758	int intlen;
 759	ulong tmp = val;
 760
 761    intlen = 1;
 762
 763    while (tmp >= base) {
 764        tmp /= base;
 765        intlen++;
 766    }
 767
 768    //allocate
 769    tmp = val;
 770
 771    string ret = new char[intlen];
 772
 773    intlen--;
 774
 775    do {
 776    	uint off = cast(uint)(tmp % base);
 777    	char replace;
 778    	if (off < 10) {
 779    		replace = cast(char)('0' + off);
 780    	}
 781    	else if (off < 36) {
 782    		off -= 10;
 783    		replace = cast(char)('a' + off);
 784    	}
 785        ret[intlen] = replace;
 786        tmp /= base;
 787        intlen--;
 788    } while (tmp != 0);
 789
 790    return ret;
 791}
 792
 793private union intFloat {
 794	int l;
 795	float f;
 796}
 797
 798private union longDouble {
 799	long l;
 800	double f;
 801}
 802
 803private union longReal {
 804	struct inner {
 805		short exp;
 806		long frac;
 807	}
 808
 809	inner l;
 810	real f;
 811}
 812
 813string ctoa(cfloat val, uint base = 10) {
 814	if (val is cfloat.infinity) {
 815		return "inf";
 816	}
 817	else if (val.re !<>= 0.0 && val.im !<>= 0.0) {
 818		return "nan";
 819	}
 820
 821	return ftoa(val.re, base) ~ " + " ~ ftoa(val.im, base) ~ "i";
 822}
 823
 824string ctoa(cdouble val, uint base = 10) {
 825	if (val is cdouble.infinity) {
 826		return "inf";
 827	}
 828	else if (val.re !<>= 0.0 && val.im !<>= 0.0) {
 829		return "nan";
 830	}
 831
 832	return dtoa(val.re, base) ~ " + " ~ ftoa(val.im, base) ~ "i";
 833}
 834
 835string ctoa(creal val, uint base = 10) {
 836	if (val is creal.infinity) {
 837		return "inf";
 838	}
 839	else if (val is creal.nan) {
 840		return "nan";
 841	}
 842
 843	return rtoa(val.re, base) ~ " + " ~ ftoa(val.im, base) ~ "i";
 844}
 845
 846string ftoa(float val, uint base = 10) {
 847	string[] foo = pftoa(val, base);
 848
 849	string ret = foo[0];
 850	if (foo[1].length > 0) {
 851		ret ~= "." ~ foo[1];
 852	}
 853	return ret;
 854}
 855
 856string[] pftoa(float val, uint base = 10) {
 857	if (val == float.infinity) {
 858		return ["inf",""];
 859	}
 860	else if (val !<>= 0.0) {
 861		return ["nan",""];
 862	}
 863	else if (val == 0.0) {
 864		return ["0",""];
 865	}
 866
 867	long mantissa;
 868	long intPart;
 869	long fracPart;
 870
 871	short exp;
 872
 873	intFloat iF;
 874	iF.f = val;
 875
 876	// Conform to the IEEE standard
 877	exp = ((iF.l >> 23) & 0xff) - 127;
 878	mantissa = (iF.l & 0x7fffff) | 0x800000;
 879	fracPart = 0;
 880	intPart = 0;
 881
 882	if (exp >= 31) {
 883		return ["0",""];
 884	}
 885	else if (exp < -23) {
 886		return ["0",""];
 887	}
 888	else if (exp >= 23) {
 889		intPart = mantissa << (exp - 23);
 890	}
 891	else if (exp >= 0) {
 892		intPart = mantissa >> (23 - exp);
 893		fracPart = (mantissa << (exp + 1)) & 0xffffff;
 894	}
 895	else { // exp < 0
 896		fracPart = (mantissa & 0xffffff) >> (-(exp + 1));
 897	}
 898
 899	string[] ret = ["",""];
 900	if (iF.l < 0) {
 901		ret[0] = "-";
 902	}
 903
 904	ret[0] ~= itoa(intPart, base);
 905
 906	for (uint k; k < 7; k++) {
 907		fracPart *= 10;
 908		ret[1] ~= cast(char)((fracPart >> 24) + '0');
 909		fracPart &= 0xffffff;
 910	}
 911	
 912	// round last digit
 913	bool roundUp = (ret[1][$-1] >= '5');
 914	ret[1] = ret[1][0..$-1];
 915
 916	while (roundUp) {
 917		// Look for a completely empty float
 918		if (ret[0].length + ret[1].length == 0) {
 919			return ["0",""];
 920		}
 921		else if (ret[1].length > 0 && ret[1][$-1] == '9') {
 922			ret[1] = ret[1][0..$-1];
 923			continue;
 924		}
 925		ret[1][$-1]++;
 926		break;
 927	}
 928
 929	// get rid of useless trailing zeroes
 930	foreach_reverse(uint i, chr; ret[1]) {
 931		if (chr != '0') {
 932			ret[1] = ret[1][0..i+1];
 933			break;
 934		}
 935	}
 936
 937	return ret;
 938}
 939
 940string dtoa(double val, uint base = 10) {
 941	string[] foo = pdtoa(val, base);
 942
 943	string ret = foo[0];
 944	if (foo[1].length > 0) {
 945		ret ~= "." ~ foo[1];
 946	}
 947	
 948	return ret;
 949}
 950
 951string[] pdtoa(double val, uint base = 10) {
 952	if (val is double.infinity) {
 953		return ["inf",""];
 954	}
 955	else if (val !<>= 0.0) {
 956		return ["nan",""];
 957	}
 958	else if (val == 0.0) {
 959		return ["0",""];
 960	}
 961
 962	long mantissa;
 963	long intPart;
 964	long fracPart;
 965
 966	long exp;
 967
 968	longDouble iF;
 969	iF.f = val;
 970
 971	// Conform to the IEEE standard
 972	exp = ((iF.l >> 52) & 0x7ff);
 973	if (exp == 0) {
 974		return ["0",""];
 975	}
 976	else if (exp == 0x7ff) {
 977		return ["inf",""];
 978	}
 979	exp -= 1023;
 980
 981	mantissa = (iF.l & 0xfffffffffffff) | 0x10000000000000;
 982	fracPart = 0;
 983	intPart = 0;
 984
 985	if (exp < -52) {
 986		return ["0",""];
 987	}
 988	else if (exp >= 52) {
 989		intPart = mantissa << (exp - 52);
 990	}
 991	else if (exp >= 0) {
 992		intPart = mantissa >> (52 - exp);
 993		fracPart = (mantissa << (exp + 1)) & 0x1fffffffffffff;
 994	}
 995	else { // exp < 0
 996		fracPart = (mantissa & 0x1fffffffffffff) >> (-(exp + 1));
 997	}
 998
 999	string ret[] = ["", ""];
1000	if (iF.l < 0) {
1001		ret[0] = "-";
1002	}
1003
1004	ret[0] ~= itoa(intPart, base);
1005
1006	for (uint k; k < 7; k++) {
1007		fracPart *= 10;
1008		ret[1] ~= cast(char)((fracPart >> 53) + '0');
1009		fracPart &= 0x1fffffffffffff;
1010	}
1011	
1012	// round last digit
1013	bool roundUp = (ret[1][$-1] >= '5');
1014	ret[1] = ret[1][0..$-1];
1015
1016	while (roundUp) {
1017		if (ret[0].length == 0 && ret[1].length == 0) {
1018			return ["0",""];
1019		}
1020		else if (ret[1][$-1] == '9') {
1021			ret[1] = ret[1][0..$-1];
1022			continue;
1023		}
1024		ret[1][$-1]++;
1025		break;
1026	}
1027
1028	// get rid of useless zeroes (and point if necessary)
1029	foreach_reverse(uint i, chr; ret[1]) {
1030		if (chr != '0') {
1031			ret[1] = ret[1][0..i+1];
1032			break;
1033		}
1034	}
1035
1036	return ret;
1037}
1038
1039string rtoa(real val, uint base = 10) {
1040	static if (real.sizeof == 10) {
1041		// Support for 80-bit extended precision
1042
1043		if (val is real.infinity) {
1044			return "inf";
1045		}
1046		else if (val !<>= 0.0) {
1047			return "nan";
1048		}
1049		else if (val == 0.0) {
1050			return "0";
1051		}
1052
1053		long mantissa;
1054		long intPart;
1055		long fracPart;
1056	
1057		long exp;
1058
1059		longReal iF;
1060		iF.f = val;
1061
1062		// Conform to the IEEE standard
1063		exp = iF.l.exp & 0x7fff;
1064		if (exp == 0) {
1065			return "0";
1066		}
1067		else if (exp == 32767) {
1068			return "inf";
1069		}
1070		exp -= 16383;
1071
1072		mantissa = iF.l.frac;
1073		fracPart = 0;
1074		intPart = 0;
1075	
1076		if (exp >= 31) {
1077			return "0";
1078		}
1079		else if (exp < -64) {
1080			return "0";
1081		}
1082		else if (exp >= 64) {
1083			intPart = mantissa << (exp - 64);
1084		}
1085		else if (exp >= 0) {
1086			intPart = mantissa >> (64 - exp);
1087			fracPart = mantissa << (exp + 1);
1088		}
1089		else { // exp < 0
1090			fracPart = mantissa >> (-(exp + 1));
1091		}
1092
1093		string ret;
1094		if (iF.l.exp < 0) {
1095			ret = "-";
1096		}
1097	
1098		ret ~= itoa(intPart, base);
1099		ret ~= '.';
1100
1101		for (uint k; k < 7; k++) {
1102			fracPart *= 10;
1103			ret ~= cast(char)((fracPart >> 64) + '0');
1104		}
1105		
1106		// round last digit
1107		bool roundUp = (ret[$-1] >= '5');
1108		ret = ret[0..$-1];
1109	
1110		while (roundUp) {
1111			if (ret.length == 0) {
1112				return "0";
1113			}
1114			else if (ret[$-1] == '.' || ret[$-1] == '9') {
1115				ret = ret[0..$-1];
1116				continue;
1117			}
1118			ret[$-1]++;
1119			break;
1120		}
1121	
1122		// get rid of useless zeroes (and point if necessary)
1123		foreach_reverse(uint i, chr; ret) {
1124			if (chr != '0' && chr != '.') {
1125				ret = ret[0..i+1];
1126				break;
1127			}
1128			else if (chr == '.') {
1129				ret = ret[0..i];
1130				break;
1131			}
1132		}
1133
1134		return ret;
1135	}
1136	else {
1137		return ftoa(cast(double)val, base);
1138	}
1139}
1140