PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/dotmars/base/convert.d

http://dotmars.googlecode.com/
D | 829 lines | 630 code | 133 blank | 66 comment | 178 complexity | 5a88078c7bff166fbcc8a6081cd0f757 MD5 | raw file
  1. /** Converter & Formatter
  2. Written in the D programming language 1.0
  3. Authors: Wei Li (oldrev@gmail.com)
  4. License: BSD
  5. Copyright: Copyright (C) 2007 by Wei Li.
  6. Date: May 17 2007
  7. */
  8. /*
  9. TODO:
  10. * ???????????
  11. */
  12. module dotmars.base.convert;
  13. import dotmars.base.typetraits;
  14. import dotmars.base.stdexcept;
  15. import dotmars.base.stdtypes;
  16. import dotmars.base.math;
  17. import dotmars.text.utf;
  18. import dotmars.base.string;
  19. import dotmars.io.console;
  20. //36????
  21. const char[36] Digitals = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  22. const char[36] DigitalsLower = "0123456789abcdefghijklmnopqrstuvwxyz";
  23. private static uint charToNumber(char c)
  24. {
  25. if(c >= '0' && c <= '9')
  26. return c - '0';
  27. else if(c >= 'A' && c <= 'Z')
  28. return c - 'A' + 10;
  29. else if(c >= 'a' && c <= 'z')
  30. return c - 'a' + 10;
  31. throw new FormatException("Invalid digital character");
  32. }
  33. ////////////////////////////////////////////////////////////////////////////////
  34. final static class BitConverter
  35. {
  36. enum Endian
  37. {
  38. Little,
  39. Big
  40. }
  41. version(LittleEndian)
  42. public const bool IsLittleEndian = true;
  43. version(BigEndian)
  44. public const bool IsLittleEndian = false;
  45. public static T reverseByteOrder(T)(T value)
  46. {
  47. static assert(IsIntegerType!(T) || IsCharType!(T) && T.sizeof > 1);
  48. static if(is(T == ushort))
  49. return fastReverseByteOrder16(value);
  50. T ret = value;
  51. ubyte* first = cast(ubyte*)(&ret);
  52. ubyte* last = first + T.sizeof - 1;
  53. while (first < last)
  54. {
  55. ubyte tmp = *first;
  56. *first = *last;
  57. *last = tmp;
  58. ++first;
  59. --last;
  60. }
  61. return ret;
  62. }
  63. private static ushort fastReverseByteOrder16(ushort value)
  64. {
  65. ushort ret = value >> 8;
  66. ret += value << 8;
  67. return ret;
  68. }
  69. public static bool toBoolean(ubyte[] bytes)
  70. in {
  71. assert(bytes !is null);
  72. }
  73. body {
  74. if(bytes.length < 1)
  75. throw new ArgumentException("toBoolean: Empty array");
  76. return bytes[0] == 0;
  77. }
  78. public static ubyte[] getBytes(T)(T value)
  79. {
  80. ubyte* ptr = cast(ubyte*)(&value);
  81. return ptr[0..value.sizeof];
  82. }
  83. }
  84. ////////////////////////////////////////////////////////////////////////////////
  85. static class Integer
  86. {
  87. //format INTs
  88. static void getChars(T)(T value, Sink!(char) sink, uint radix = 10, uint precision = 0, bool lower = false)
  89. in {
  90. static assert(IsIntegerType!(T));
  91. assert(radix >= 2 && radix <= 36);
  92. }
  93. body {
  94. //????2???
  95. char[T.sizeof * 8 + 1] a;
  96. size_t index = a.length - 1;
  97. T u = abs(value);
  98. do {
  99. a[index--] =
  100. lower ? DigitalsLower[u % radix] : Digitals[u % radix];
  101. }while(u /= radix)
  102. if(value < 0)a[index--] = '-';
  103. size_t len = a[index + 1 .. $].length;
  104. if(precision > len)
  105. {
  106. for(size_t j = 0; j < precision - len; j++)
  107. sink('0');
  108. }
  109. foreach(char c; a[index + 1 .. $])
  110. sink(c);
  111. }
  112. static string toString(T)(T value, uint radix = 10, uint precision = 0, bool lower = false)
  113. {
  114. string result;
  115. Sink!(char) sink =
  116. (char c) { result ~= c; };
  117. getChars!(T)(value, sink, radix, precision, lower);
  118. return result;
  119. }
  120. static T parse(T)(string str)
  121. {
  122. bool sign = false;
  123. T value = 0;
  124. uint radix = 10;
  125. size_t i = 0;
  126. for(; i < str.length; i++)
  127. {
  128. char c = str[i];
  129. //????
  130. if(c == ' ') continue;
  131. if(c == '-')
  132. {
  133. sign = true;
  134. continue;
  135. }
  136. if(c == '+')
  137. continue;
  138. //???????
  139. //???????
  140. if(c != '0')break;
  141. //??????? '0'
  142. if(i >= str.length - 1)
  143. break;
  144. c = str[++i];
  145. switch(c)
  146. {
  147. case 'x':
  148. case 'X':
  149. radix = 16;
  150. i++;
  151. break;
  152. case 'b':
  153. case 'B':
  154. radix = 2;
  155. i++;
  156. break;
  157. //8??
  158. default:
  159. radix = 8;
  160. break;
  161. }
  162. break;
  163. }
  164. //??????????
  165. for(; i < str.length; i++)
  166. {
  167. char c = str[i];
  168. uint d = charToNumber(c);
  169. if(d < radix)
  170. value = value * radix + d;
  171. else //???????????
  172. throw new FormatException("toInteger: invalid character");
  173. }
  174. if(sign)value = -value;
  175. return value;
  176. }
  177. }
  178. ///////////////////////////////////////////////////////////////////////////////
  179. static class Float
  180. {
  181. static void getChars(T)(T value, Sink!(char) sink, uint precision = 6,
  182. bool scientific = false, bool lower = false)
  183. in {
  184. static assert(IsFloatType!(T));
  185. assert(sink !is null);
  186. }
  187. body {
  188. T x = abs(value);
  189. const T radix = 10.0;
  190. int exp = cast(int)log10(x);
  191. int intDigits = exp + 1;
  192. if(exp < 0)
  193. {
  194. intDigits = -exp + 1;
  195. x *= pow(radix, cast(T)-exp);
  196. }
  197. else
  198. x /= pow(radix, cast(T)exp);
  199. if(scientific)
  200. intDigits = 1;
  201. int digits = intDigits + precision;
  202. if(value < 0)sink('-');
  203. for(size_t i = 0; i < digits; i++)
  204. {
  205. if(i == intDigits)
  206. sink('.');
  207. int digit = cast(int)x;
  208. sink('0' + digit);
  209. x = (x - digit) * radix;
  210. }
  211. if(scientific)
  212. {
  213. if(lower)
  214. sink('e');
  215. else
  216. sink('E');
  217. if(exp > 9)
  218. {
  219. sink('0' + exp / 10);
  220. sink('0' + exp % 10);
  221. }
  222. else
  223. sink('0' + exp);
  224. }
  225. }
  226. static string toString(T)(T value, uint precision = 6, bool scientific = false, bool lower = false)
  227. {
  228. string result;
  229. Sink!(char) sink = (char c) { result ~= c; };
  230. getChars!(T)(value, sink, precision, scientific, lower);
  231. return result;
  232. }
  233. }
  234. //////////////////////////////////////////////////////////////////////////////
  235. static final class Convert
  236. {
  237. public static string toString(byte value, uint radix = 10) {
  238. return Integer.toString!(int)(value, radix);
  239. }
  240. public static string toString(short value, uint radix = 10) {
  241. return Integer.toString!(int)(value, radix);
  242. }
  243. public static string toString(int value, uint radix = 10) {
  244. return Integer.toString!(int)(value, radix);
  245. }
  246. public static string toString(long value, uint radix = 10) {
  247. return Integer.toString!(long)(value, radix);
  248. }
  249. public static string toString(ubyte value, uint radix = 10) {
  250. return Integer.toString!(int)(value, radix);
  251. }
  252. public static string toString(ushort value, uint radix = 10) {
  253. return Integer.toString!(int)(value, radix);
  254. }
  255. public static string toString(uint value, uint radix = 10) {
  256. return Integer.toString!(uint)(value, radix);
  257. }
  258. public static string toString(ulong value, uint radix = 10) {
  259. return Integer.toString!(ulong)(value, radix);
  260. }
  261. // floats
  262. public static string toString(float value) {
  263. return Float.toString!(float)(value);
  264. }
  265. public static string toString(double value) {
  266. return Float.toString!(float)(value);
  267. }
  268. public static string toString(real value) {
  269. return Float.toString!(real)(value);
  270. }
  271. //imaginary numbers
  272. public static string toString(ifloat value) {
  273. return Float.toString!(float)(cast(float)value) ~ 'i';
  274. }
  275. public static string toString(idouble value) {
  276. return Float.toString!(double)(cast(double)value) ~ 'i';
  277. }
  278. public static string toString(ireal value) {
  279. return Float.toString!(real)(cast(real)value) ~ 'i';
  280. }
  281. //complex numbers
  282. public static string toString(cfloat value){
  283. return Float.toString!(float)(cast(float)value.re) ~
  284. '+' ~ Float.toString!(float)(cast(float)value.im) ~ 'i';
  285. }
  286. public static string toString(cdouble value){
  287. return Float.toString!(double)(cast(double)value.re) ~
  288. '+' ~ Float.toString!(double)(cast(double)value.im) ~ 'i';
  289. }
  290. public static string toString(creal value){
  291. return Float.toString!(real)(cast(real)value.re) ~
  292. '+' ~ Float.toString!(real)(cast(real)value.im) ~ 'i';
  293. }
  294. public static T fromString(T)(string str)
  295. {
  296. static if(IsIntegerType!(T))
  297. {
  298. return Integer.parse!(T)(str);
  299. }
  300. else
  301. {
  302. static assert(false, "not implemented");
  303. }
  304. }
  305. private static bool isDecimalChar(char c)
  306. {
  307. if(c >= '0' && c <= '9')return true;
  308. return false;
  309. }
  310. }
  311. ///////////////////////////////////////////////////////////////////////////////
  312. static class Formatter
  313. {
  314. const size_t MaxArguments = 16;
  315. static void format(Sink!(char) sink, string fmt, ...)
  316. {
  317. format(sink, fmt, _argptr, _arguments);
  318. }
  319. static void format(Sink!(char) sink, Argument argPtr, TypeInfo[] argtis, string fmt)
  320. in {
  321. assert(sink !is null);
  322. assert(fmt !is null);
  323. }
  324. body {
  325. assert(argtis.length <= MaxArguments, "Formatter.format(): Too many arguments");
  326. Argument[MaxArguments] args = void;
  327. //??????
  328. foreach (size_t i, ti; argtis)
  329. {
  330. args[i] = argPtr;
  331. //??? stdarg.d?????CPU?????????
  332. argPtr += (ti.tsize + size_t.sizeof - 1) & ~ (size_t.sizeof - 1);
  333. }
  334. doFormat(sink, fmt, args, argtis);
  335. }
  336. static string format(string fmt, ...) {
  337. string ret;
  338. Sink!(char) sink = (char c){ ret ~= c; } ;
  339. format(sink, fmt, _argptr, _arguments);
  340. return ret;
  341. }
  342. static size_t sprint(string dest, Argument argPtr, TypeInfo[] argtis, string fmt)
  343. in {
  344. assert(dest !is null);
  345. }
  346. body {
  347. size_t n = 0;
  348. Sink!(char) sink =
  349. (char c) { dest[n++] = c; } ;
  350. format(sink, fmt, argPtr, argtis);
  351. return n;
  352. }
  353. static size_t sprint(string dest, string fmt, ...) {
  354. return sprint(dest, fmt, _argptr, _arguments);
  355. }
  356. private static void formatString(T)(Sink!(char) sink, string fmt, T str)
  357. {
  358. static assert(is(T == string) || is(T == wstring) || is( T == dstring));
  359. if(fmt.length > 0)
  360. throw new FormatException("Not Supported");
  361. static if(is(T == string)) {
  362. foreach(char c; str)
  363. sink(c);
  364. }
  365. else static if(is(T == wstring)) {
  366. foreach(wchar c; str)
  367. .toUtf8(c, sink);
  368. }
  369. else static if(is(T == dstring)) {
  370. foreach(dchar c; str)
  371. .toUtf8(c, sink);
  372. }
  373. }
  374. private static void formatInteger(T)( Sink!(char) sink, string fmtstr, T i)
  375. in {
  376. static assert(IsIntegerType!(T));
  377. }
  378. body {
  379. bool lower = false;
  380. uint radix = 10;
  381. int precision = 0;
  382. if(fmtstr !is null && fmtstr.length >= 1)
  383. {
  384. char fmt = fmtstr[0];
  385. switch(fmt)
  386. {
  387. case 'x':
  388. radix = 16;
  389. lower = true;
  390. break;
  391. case 'X':
  392. radix = 16;
  393. break;
  394. case 'g':
  395. case 'G':
  396. case 'D':
  397. case 'd':
  398. radix = 10;
  399. break;
  400. default:
  401. throw new FormatException("Syntax error");
  402. break;
  403. }
  404. if(fmtstr.length > 1)
  405. {
  406. size_t l = 0;
  407. precision = extractDecimal(fmtstr[1..$], l);
  408. if(precision < 0)
  409. throw new FormatException("Invalid precision");
  410. }
  411. }
  412. Integer.getChars!(T)(i, sink, radix, precision, lower);
  413. }
  414. private static void formatFloat(T)(Sink!(char) sink, string fmtstr, T f)
  415. {
  416. uint precision = 6;
  417. bool scientific = false;
  418. bool lower = false;
  419. if(fmtstr !is null && fmtstr.length >= 1)
  420. {
  421. char fmt = fmtstr[0];
  422. switch(fmt)
  423. {
  424. case 'E':
  425. scientific = true;
  426. lower = false;
  427. break;
  428. case 'e':
  429. scientific = true;
  430. lower = true;
  431. break;
  432. case 'g':
  433. case 'G':
  434. case 'd':
  435. case 'D':
  436. scientific = false;
  437. break;
  438. default:
  439. throw new FormatException("Syntax error");
  440. break;
  441. }
  442. if(fmtstr.length > 1)
  443. {
  444. size_t l = 0;
  445. precision = extractDecimal(fmtstr[1..$], l);
  446. if(precision < 0)
  447. throw new FormatException("Invalid precision");
  448. }
  449. }
  450. Float.getChars!(T)(f, sink, precision, scientific, lower);
  451. }
  452. private static void putSpaces(Sink!(char) sink, uint n) {
  453. for(uint i = 0; i < n; i++)
  454. sink(' ');
  455. }
  456. private static uint extractDecimal(string str, out size_t len) {
  457. assert(str !is null);
  458. len = 0;
  459. foreach(char c; str)
  460. {
  461. if(c >= '0' && c <= '9') len++;
  462. else break;
  463. }
  464. if(len == 0)
  465. throw new FormatException("Format: syntax error");
  466. return Convert.fromString!(uint)(str[0 .. len]);
  467. }
  468. private static size_t findRightBrach(string str)
  469. {
  470. assert(str !is null);
  471. foreach(size_t i, char c; str) {
  472. if(c == '}')
  473. return i;
  474. }
  475. // "{}" have not matched.
  476. throw new FormatException("Format: Syntax error");
  477. }
  478. private static void doFormat(Sink!(char) sink, string fmt, Argument[] args, TypeInfo[] ti)
  479. {
  480. for(size_t i = 0; i < fmt.length;)
  481. {
  482. // skip the escape char
  483. for(; i < fmt.length && fmt[i] != '{'; i++)
  484. sink(fmt[i]);
  485. if(i >= fmt.length)break;
  486. // "{{"
  487. if(i < fmt.length - 1 && fmt[i + 1] == '{')
  488. {
  489. i += 2;
  490. sink('{');
  491. continue;
  492. }
  493. size_t beginBrach = i;
  494. size_t endBrach = i + findRightBrach(fmt[i .. $]);
  495. // parsing the format string
  496. doParse(sink, fmt[beginBrach + 1 .. endBrach], args, ti);
  497. i = endBrach + 1;
  498. }
  499. }
  500. //???????????
  501. private static void doParse(Sink!(char) sink, string fmt, Argument[] args, TypeInfo[] ti)
  502. in {
  503. assert(fmt !is null);
  504. assert(args !is null);
  505. assert(ti !is null);
  506. }
  507. body {
  508. //{INDEX,+/-ALIGNMENT:FORMAT}
  509. size_t i = 0, len = 0;
  510. // skip spaces
  511. while(i < fmt.length && fmt[i] == ' ') i++;
  512. // extract the index
  513. uint index = extractDecimal(fmt[i..$], len);
  514. if(index >= ti.length)
  515. throw new FormatException("Invalid index of place holder");
  516. i += len;
  517. //skip spaces
  518. while(i < fmt.length && fmt[i] == ' ') i++;
  519. //deal with alignment
  520. int alignment = 0;
  521. if(i < fmt.length && fmt[i] == ',')
  522. {
  523. i++;
  524. while(i < fmt.length && fmt[i] == ' ') i++;
  525. bool sign = false;
  526. if(fmt[i] == '-')
  527. {
  528. i++;
  529. sign = true;
  530. }
  531. if(fmt[i] == '+')
  532. i++;
  533. alignment = extractDecimal(fmt[i..$], len);
  534. if(sign) alignment = -alignment;
  535. i += len;
  536. }
  537. //skip spaces
  538. while(i < fmt.length && fmt[i] == ' ') i++;
  539. string fmtstr = null;
  540. if(i < fmt.length && fmt[i] == ':')
  541. {
  542. i++;
  543. while(i < fmt.length && fmt[i] == ' ') i++;
  544. size_t l = i;
  545. while(l < fmt.length && fmt[l] != ' ') l++;
  546. fmtstr = fmt[i .. l];
  547. }
  548. handleAlignment(sink, alignment, fmtstr, args[index], ti[index]);
  549. }
  550. private static void formatDisp(Sink!(char) sink, string fmtstr, Argument arg, TypeInfo ti)
  551. in {
  552. assert(sink !is null);
  553. assert(ti !is null);
  554. }
  555. body {
  556. // integers
  557. if(ti == typeid(byte)) {
  558. byte* pval = cast(byte*)arg;
  559. formatInteger!(byte)(sink, fmtstr, *pval);
  560. }
  561. else if(ti == typeid(ubyte)) {
  562. ubyte* pval = cast(ubyte*)arg;
  563. formatInteger!(ubyte)(sink, fmtstr, *pval);
  564. }
  565. else if (ti == typeid(short)) {
  566. short* pval = cast(short*)arg;
  567. formatInteger!(short)(sink, fmtstr, *pval);
  568. }
  569. else if(ti == typeid(ushort)) {
  570. ushort* pval = cast(ushort*)arg;
  571. formatInteger!(ushort)(sink, fmtstr, *pval);
  572. }
  573. else if(ti == typeid(int)) {
  574. int* pval = cast(int*)arg;
  575. formatInteger!(int)(sink, fmtstr, *pval);
  576. }
  577. else if(ti == typeid(uint)) {
  578. uint* pval = cast(uint*)arg;
  579. formatInteger!(uint)(sink, fmtstr, *pval);
  580. }
  581. else if(ti == typeid(long)) {
  582. long* pval = cast(long*)arg;
  583. formatInteger!(long)(sink, fmtstr, *pval);
  584. }
  585. else if(ti == typeid(ulong)) {
  586. ulong* pval = cast(ulong*)arg;
  587. formatInteger!(ulong)(sink, fmtstr, *pval);
  588. }
  589. // floats
  590. else if(ti == typeid(float)) {
  591. float* pval = cast(float*)arg;
  592. formatFloat!(float)(sink, fmtstr, *pval);
  593. }
  594. else if(ti == typeid(double)) {
  595. double* pval = cast(double*)arg;
  596. formatFloat!(double)(sink, fmtstr,*pval);
  597. }
  598. else if(ti == typeid(real)) {
  599. real* pval = cast(real*)arg;
  600. formatFloat!(real)(sink, fmtstr, *pval);
  601. }
  602. else if(ti == typeid(ifloat))
  603. {
  604. float* pval = cast(float*)arg;
  605. formatFloat!(float)(sink, fmtstr, *pval);
  606. }
  607. else if(ti == typeid(idouble))
  608. {
  609. double* pval = cast(double*)arg;
  610. formatFloat!(double)(sink, fmtstr, *pval);
  611. }
  612. else if(ti == typeid(ireal))
  613. {
  614. real* pval = cast(real*)arg;
  615. formatFloat!(real)(sink, fmtstr, *pval);
  616. }
  617. /*
  618. else if(ti == typeid(cfloat))
  619. {
  620. cfloat* pval = cast(cfloat*)arg;
  621. formatOne(fmtstr, sink, *pval);
  622. }
  623. else if(ti == typeid(cdouble))
  624. {
  625. cdouble* pval = cast(cdouble*)arg;
  626. formatOne(fmtstr, sink, *pval);
  627. }
  628. else if(ti == typeid(creal))
  629. {
  630. creal* pval = cast(creal*)arg;
  631. formatOne(fmtstr, sink, *pval);
  632. }*/
  633. //string type
  634. else if(ti == typeid(string)) {
  635. string *pstr = cast(string*)arg;
  636. formatString!(string)(sink, fmtstr, *pstr);
  637. }
  638. else if(ti == typeid(wstring)) {
  639. wstring *pstr = cast(wstring*)arg;
  640. formatString!(wstring)(sink, fmtstr, *pstr);
  641. }
  642. else if(ti == typeid(dstring)) {
  643. dstring *pstr = cast(dstring*)arg;
  644. formatString!(dstring)(sink, fmtstr, *pstr);
  645. }
  646. //character type
  647. else if(ti == typeid(char)) {
  648. char *pstr = cast(char*)arg;
  649. formatString!(string)(sink, fmtstr, pstr[0..1]);
  650. }
  651. else if(ti == typeid(wchar)) {
  652. wchar *pstr = cast(wchar*)arg;
  653. formatString!(wstring)(sink, fmtstr, pstr[0..1]);
  654. }
  655. else if(ti == typeid(dchar)) {
  656. dchar *pstr = cast(dchar*)arg;
  657. formatString!(dstring)(sink, fmtstr, pstr[0..1]);
  658. }
  659. //boolean
  660. else if(ti == typeid(bool)) {
  661. bool *pval = cast(bool*)arg;
  662. formatString!(string)(sink, fmtstr, *pval ? "true" : "false");
  663. }
  664. //class
  665. else if(ti.classinfo.name == "TypeInfo_Class")
  666. {
  667. Object* pobj = cast(Object*) arg;
  668. version(Tango) {
  669. string str = (*pobj) is null ? "<null>" : (*pobj).toUtf8;
  670. }
  671. else {
  672. string str = (*pobj) is null ? "<null>" : (*pobj).toString;
  673. }
  674. formatString!(string)(sink, fmtstr, str);
  675. }
  676. else {
  677. throw new FormatException("Unknown type");
  678. }
  679. }
  680. private static void handleAlignment(Sink!(char) sink, int alignment,
  681. string fmt, Argument arg, TypeInfo ti)
  682. {
  683. int count = 0;
  684. Sink!(char) drySink = (char c) { count++; };
  685. formatDisp(drySink, fmt, arg, ti);
  686. int padding = abs(alignment) - count;
  687. if(padding > 0 && alignment > 0)
  688. putSpaces(sink, padding);
  689. formatDisp(sink, fmt, arg, ti);
  690. //right align
  691. if(padding > 0 && alignment < 0)
  692. putSpaces(sink, padding);
  693. }
  694. }