1
2
3
4
5
6 using System.Concepts;
7
8 namespace System
9 {
10 public class ConversionException : Exception
11 {
12 public nothrow ConversionException(const string& message) : base(message)
13 {
14 }
15 }
16
17 public void ThrowConversionException(const string& message)
18 {
19 throw ConversionException(message);
20 }
21
22 public nothrow string ToString<I, U>(I x) where I is SignedInteger and U is UnsignedInteger and ExplicitlyConvertible<I, U> and ExplicitlyConvertible<U, byte>
23 {
24 string s;
25 U u = 0u;
26 bool neg = x < 0;
27 if (neg)
28 {
29 u = -cast<U>(x);
30 }
31 else
32 {
33 u = cast<U>(x);
34 }
35 do
36 {
37 s.Append(cast<char>(cast<byte>('0') + cast<byte>(u % 10u)));
38 u = u / 10u;
39 }
40 while (u != 0u);
41 if (neg)
42 {
43 s.Append('-');
44 }
45 Reverse(s.Begin(), s.End());
46 return s;
47 }
48
49 public nothrow string ToString<U>(U x) where U is UnsignedInteger and ExplicitlyConvertible<U, byte>
50 {
51 string s;
52 do
53 {
54 s.Append(cast<char>(cast<byte>('0') + cast<byte>(x % 10u)));
55 x = x / 10u;
56 }
57 while (x != 0u);
58 Reverse(s.Begin(), s.End());
59 return s;
60 }
61
62 public nothrow string ToString(sbyte x)
63 {
64 return ToString(cast<int>(x));
65 }
66
67 public nothrow string ToString(byte x)
68 {
69 return ToString(cast<uint>(x));
70 }
71
72 public nothrow string ToString(short x)
73 {
74 return ToString(cast<int>(x));
75 }
76
77 public nothrow string ToString(ushort x)
78 {
79 return ToString(cast<uint>(x));
80 }
81
82 public nothrow string ToString(int x)
83 {
84 return ToString<int, uint>(x);
85 }
86
87 public nothrow string ToString(uint x)
88 {
89 return ToString<uint>(x);
90 }
91
92 public nothrow string ToString(long x)
93 {
94 return ToString<long, ulong>(x);
95 }
96
97 public nothrow string ToString(ulong x)
98 {
99 return ToString<ulong>(x);
100 }
101
102 public nothrow string ToString(float f)
103 {
104 return ToString(cast<double>(f));
105 }
106
107 public nothrow string ToString(double x, int maxNumDecimals)
108 {
109 return ToString(x, 0, maxNumDecimals);
110 }
111
112 public nothrow string ToString(double x)
113 {
114 return ToString(x, 15);
115 }
116
117 public nothrow string ToString(double x, int minNumDecimals, int maxNumDecimals)
118 {
119 string result;
120 if (x < 0)
121 {
122 x = -x;
123 result.Append('-');
124 }
125 result.Append(ToString(cast<int>(x)));
126 double d = x - cast<int>(x);
127 if (d > 0 || minNumDecimals > 0)
128 {
129 result.Append('.');
130 for (int i = 0; (d > 0 || i < minNumDecimals) && i < maxNumDecimals; ++i;)
131 {
132 d = 10 * d;
133 int digit = cast<int>(d) % 10;
134 result.Append(cast<char>(cast<int>('0') + digit));
135 d = d - cast<int>(d);
136 }
137 }
138 return result;
139 }
140
141 public nothrow string ToString(char c)
142 {
143 return string(c);
144 }
145
146 public string ToString(wchar c)
147 {
148 wstring s(c);
149 return ToUtf8(s);
150 }
151
152 public string ToString(uchar c)
153 {
154 ustring s(c);
155 return ToUtf8(s);
156 }
157
158 public nothrow string ToString(bool b)
159 {
160 if (b)
161 {
162 return "true";
163 }
164 return "false";
165 }
166
167 public inline char HexChar(byte nibble)
168 {
169 if ((nibble & ~0x0Fu) != 0u)
170 {
171 ThrowInvalidParameterException();
172 }
173 const char* hex = "0123456789ABCDEF";
174 return hex[nibble];
175 }
176
177 public nothrow string ToHexString<U>(U x) where U is UnsignedInteger and ExplicitlyConvertible<U, byte>
178 {
179 string s;
180 long n = sizeof(x);
181 for (long i = 0; i < n; ++i;)
182 {
183 byte b = cast<byte>(x & 0xFFu);
184 s.Append(HexChar(b & 0x0Fu));
185 s.Append(HexChar(b >> 4u));
186 x = x >> 8u;
187 }
188 Reverse(s.Begin(), s.End());
189 return s;
190 }
191
192 public nothrow string ToHexString(byte b)
193 {
194 string s;
195 s.Append(HexChar(b >> 4u));
196 s.Append(HexChar(b & 0x0Fu));
197 return s;
198 }
199
200 public nothrow string ToHexString(ushort u)
201 {
202 return ToHexString<ushort>(u);
203 }
204
205 public nothrow string ToHexString(uint u)
206 {
207 return ToHexString<uint>(u);
208 }
209
210 public nothrow string ToHexString(ulong u)
211 {
212 return ToHexString<ulong>(u);
213 }
214
215 public nothrow string ToOctalString(ulong x)
216 {
217 return ToOctalString(x, 0);
218 }
219
220 public nothrow string ToOctalString(ulong x, int minDigits)
221 {
222 if (minDigits < 0)
223 {
224 ThrowInvalidParameterException();
225 }
226 string s;
227 ++minDigits;
228 do
229 {
230 s.Append(cast<char>(cast<int>(x & 7u) + cast<int>('0')));
231 x = x >> 3u;
232 if (minDigits > 0)
233 {
234 --minDigits;
235 }
236 }
237 while (minDigits != 0 || x != 0u);
238 if (s[s.Length() - 1] != '0')
239 {
240 s.Append('0');
241 }
242 Reverse(s.Begin(), s.End());
243 return s;
244 }
245
246 public nothrow bool ParseSigned<T>(const string& s, T& x) where T is SignedInteger
247 {
248 x = cast<T>(0);
249 if (s.IsEmpty()) return false;
250 bool negative = false;
251 int state = 0;
252 for (char c : s)
253 {
254 switch (state)
255 {
256 case 0:
257 {
258 if (c == '+')
259 {
260 state = 1;
261 }
262 else if (c == '-')
263 {
264 negative = true;
265 state = 1;
266 }
267 else if (c >= '0' && c <= '9')
268 {
269 x = cast<T>(c) - cast<T>('0');
270 state = 1;
271 }
272 else
273 {
274 return false;
275 }
276 break;
277 }
278 case 1:
279 {
280 if (c >= '0' && c <= '9')
281 {
282 x = 10 * x + cast<T>(c) - cast<T>('0');
283 }
284 else
285 {
286 return false;
287 }
288 break;
289 }
290 }
291 }
292 if (state != 1)
293 {
294 return false;
295 }
296 else
297 {
298 if (negative)
299 {
300 x = -x;
301 }
302 return true;
303 }
304 }
305
306 public nothrow bool ParseUnsigned<T>(const string& s, T& x) where T is UnsignedInteger
307 {
308 x = cast<T>(0u);
309 if (s.IsEmpty())
310 {
311 return false;
312 }
313 int state = 0;
314 for (char c : s)
315 {
316 switch (state)
317 {
318 case 0:
319 {
320 if (c == '+')
321 {
322 state = 1;
323 }
324 else if (c >= '0' && c <= '9')
325 {
326 x = cast<T>(c) - cast<T>('0');
327 state = 1;
328 }
329 else
330 {
331 return false;
332 }
333 break;
334 }
335 case 1:
336 {
337 if (c >= '0' && c <= '9')
338 {
339 x = 10u * x + cast<T>(c) - cast<T>('0');
340 }
341 else
342 {
343 return false;
344 }
345 break;
346 }
347 }
348 }
349 if (state != 1)
350 {
351 return false;
352 }
353 return true;
354 }
355
356 public nothrow bool ParseHex<T>(const string& s, T& x) where T is UnsignedInteger
357 {
358 x = cast<T>(0u);
359 if (s.IsEmpty())
360 {
361 return false;
362 }
363 for (char c : s)
364 {
365 if (c >= '0' && c <= '9')
366 {
367 x = 16u * x + (cast<T>(c) - cast<T>('0'));
368 }
369 else if (c >= 'a' && c <= 'f')
370 {
371 x = 16u * x + 10u + (cast<T>(c) - cast<T>('a'));
372 }
373 else if (c >= 'A' && c <= 'F')
374 {
375 x = 16u * x + 10u + (cast<T>(c) - cast<T>('A'));
376 }
377 else
378 {
379 return false;
380 }
381 }
382 return true;
383 }
384
385 public nothrow bool ParseFloating<T>(const string& s, T& x)
386 {
387 x = cast<T>(0.0);
388 if (s.IsEmpty())
389 {
390 return false;
391 }
392 bool negative = false;
393 int start = 0;
394 if (s[0] == '+')
395 {
396 ++start;
397 }
398 else if (s[0] == '-')
399 {
400 negative = true;
401 ++start;
402 }
403 int state = 0;
404 T d = cast<T>(10.0);
405 int exponent = 0;
406 bool negativeExponent = false;
407 long n = s.Length();
408 for (long i = start; i < n; ++i;)
409 {
410 char c = s[i];
411 switch (state)
412 {
413 case 0:
414 {
415 if (c >= '0' && c <= '9')
416 {
417 x = 10 * x + (cast<int>(c) - cast<int>('0'));
418 }
419 else if (c == '.')
420 {
421 state = 1;
422 }
423 else if (c == 'e' || c == 'E')
424 {
425 state = 2;
426 }
427 else
428 {
429 return false;
430 }
431 break;
432 }
433 case 1:
434 {
435 if (c >= '0' && c <= '9')
436 {
437 x = x + (cast<int>(c) - cast<int>('0')) / d;
438 d = 10 * d;
439 }
440 else if (c == 'e' || c == 'E')
441 {
442 state = 2;
443 }
444 else
445 {
446 return false;
447 }
448 break;
449 }
450 case 2:
451 {
452 if (c == '+')
453 {
454 state = 3;
455 }
456 else if (c == '-')
457 {
458 negativeExponent = true;
459 state = 3;
460 }
461 else if (c >= '0' && c <= '9')
462 {
463 exponent = cast<int>(c) - cast<int>('0');
464 state = 3;
465 }
466 else
467 {
468 return false;
469 }
470 break;
471 }
472 case 3:
473 {
474 if (c >= '0' && c <= '9')
475 {
476 exponent = 10 * exponent + (cast<int>(c) - cast<int>('0'));
477 }
478 else
479 {
480 return false;
481 }
482 break;
483 }
484 }
485 }
486 if (negative)
487 {
488 x = -x;
489 }
490 if (exponent != 0)
491 {
492 if (negativeExponent)
493 {
494 exponent = -exponent;
495 }
496 x = x * cast<T>(Pow(10.0, exponent));
497 }
498 return true;
499 }
500
501 public nothrow bool ParseSByte(const string& s, sbyte& x)
502 {
503 return ParseSigned<sbyte>(s, x);
504 }
505
506 public sbyte ParseSByte(const string& s)
507 {
508 sbyte x;
509 if (ParseSByte(s, x))
510 {
511 return x;
512 }
513 else
514 {
515 ThrowConversionException("cannot parse sbyte from string '" + s + "'");
516 }
517 return 0;
518 }
519
520 public nothrow bool ParseByte(const string& s, byte& x)
521 {
522 return ParseUnsigned<byte>(s, x);
523 }
524
525 public byte ParseByte(const string& s)
526 {
527 byte x;
528 if (ParseByte(s, x))
529 {
530 return x;
531 }
532 else
533 {
534 ThrowConversionException("cannot parse byte from string '" + s + "'");
535 }
536 return 0u;
537 }
538
539 public nothrow bool ParseShort(const string& s, short& x)
540 {
541 return ParseSigned<short>(s, x);
542 }
543
544 public short ParseShort(const string& s)
545 {
546 short x;
547 if (ParseShort(s, x))
548 {
549 return x;
550 }
551 else
552 {
553 ThrowConversionException("cannot parse short from string '" + s + "'");
554 }
555 return 0;
556 }
557
558 public nothrow bool ParseUShort(const string& s, ushort& x)
559 {
560 return ParseUnsigned<ushort>(s, x);
561 }
562
563 public ushort ParseUShort(const string& s)
564 {
565 ushort x;
566 if (ParseUShort(s, x))
567 {
568 return x;
569 }
570 else
571 {
572 ThrowConversionException("cannot parse ushort from string '" + s + "'");
573 }
574 return 0u;
575 }
576
577 public nothrow bool ParseInt(const string& s, int& x)
578 {
579 return ParseSigned<int>(s, x);
580 }
581
582 public int ParseInt(const string& s)
583 {
584 int x;
585 if (ParseInt(s, x))
586 {
587 return x;
588 }
589 else
590 {
591 ThrowConversionException("cannot parse int from string '" + s + "'");
592 }
593 return 0;
594 }
595
596 public nothrow bool ParseUInt(const string& s, uint& x)
597 {
598 return ParseUnsigned<uint>(s, x);
599 }
600
601 public uint ParseUInt(const string& s)
602 {
603 uint x;
604 if (ParseUInt(s, x))
605 {
606 return x;
607 }
608 else
609 {
610 ThrowConversionException("cannot parse uint from string '" + s + "'");
611 }
612 return 0u;
613 }
614
615 public nothrow bool ParseLong(const string& s, long& x)
616 {
617 return ParseSigned<long>(s, x);
618 }
619
620 public long ParseLong(const string& s)
621 {
622 long x;
623 if (ParseLong(s, x))
624 {
625 return x;
626 }
627 else
628 {
629 ThrowConversionException("cannot parse long from string '" + s + "'");
630 }
631 return 0;
632 }
633
634 public nothrow bool ParseULong(const string& s, ulong& x)
635 {
636 return ParseUnsigned<ulong>(s, x);
637 }
638
639 public ulong ParseULong(const string& s)
640 {
641 ulong x;
642 if (ParseULong(s, x))
643 {
644 return x;
645 }
646 else
647 {
648 ThrowConversionException("cannot parse ulong from string '" + s + "'");
649 }
650 return 0u;
651 }
652
653 public nothrow bool ParseFloat(const string& s, float& x)
654 {
655 return ParseFloating<float>(s, x);
656 }
657
658 public float ParseFloat(const string& s)
659 {
660 float x;
661 if (ParseFloat(s, x))
662 {
663 return x;
664 }
665 else
666 {
667 ThrowConversionException("cannot parse float from string '" + s + "'");
668 }
669 return 0.0f;
670 }
671
672 public nothrow bool ParseDouble(const string& s, double& x)
673 {
674 return ParseFloating<double>(s, x);
675 }
676
677 public double ParseDouble(const string& s)
678 {
679 double x;
680 if (ParseDouble(s, x))
681 {
682 return x;
683 }
684 else
685 {
686 ThrowConversionException("cannot parse double from string '" + s + "'");
687 }
688 return 0.0;
689 }
690
691 public nothrow bool ParseBool(const string& s, bool& x)
692 {
693 if (s == "true")
694 {
695 x = true;
696 return true;
697 }
698 else if (s == "false")
699 {
700 x = false;
701 return true;
702 }
703 else
704 {
705 x = false;
706 return false;
707 }
708 }
709
710 public bool ParseBool(const string& s)
711 {
712 bool x;
713 if (ParseBool(s, x))
714 {
715 return x;
716 }
717 else
718 {
719 ThrowConversionException("cannot parse bool from string '" + s + "'");
720 }
721 return false;
722 }
723
724 public nothrow bool ParseHexByte(const string& s, byte& x)
725 {
726 return ParseHex<byte>(s, x);
727 }
728
729 public byte ParseHexByte(const string& s)
730 {
731 byte x;
732 if (ParseHexByte(s, x))
733 {
734 return x;
735 }
736 else
737 {
738 ThrowConversionException("cannot parse hex byte from string '" + s + "'");
739 }
740 return 0u;
741 }
742
743 public nothrow bool ParseHexUShort(const string& s, ushort& x)
744 {
745 return ParseHex<ushort>(s, x);
746 }
747
748 public ushort ParseHexUShort(const string& s)
749 {
750 ushort x;
751 if (ParseHexUShort(s, x))
752 {
753 return x;
754 }
755 else
756 {
757 ThrowConversionException("cannot parse hex ushort from string '" + s + "'");
758 }
759 return 0u;
760 }
761
762 public nothrow bool ParseHexUInt(const string& s, uint& x)
763 {
764 return ParseHex<uint>(s, x);
765 }
766
767 public uint ParseHexUInt(const string& s)
768 {
769 uint x;
770 if (ParseHexUInt(s, x))
771 {
772 return x;
773 }
774 else
775 {
776 ThrowConversionException("cannot parse hex uint from string '" + s + "'");
777 }
778 return 0u;
779 }
780
781 public nothrow bool ParseHexULong(const string& s, ulong& x)
782 {
783 return ParseHex<ulong>(s, x);
784 }
785
786 public ulong ParseHexULong(const string& s)
787 {
788 ulong x;
789 if (ParseHexULong(s, x))
790 {
791 return x;
792 }
793 else
794 {
795 ThrowConversionException("cannot parse hex ulong from string '" + s + "'");
796 }
797 return 0u;
798 }
799
800 public nothrow bool ParseOctal(const string& s, ulong& x)
801 {
802 x = 0u;
803 for (char c : s)
804 {
805 if (c >= '0' && c <= '7')
806 {
807 x = 8u * x + cast<ulong>(cast<int>(c) - cast<int>('0'));
808 }
809 else
810 {
811 return false;
812 }
813 }
814 return true;
815 }
816
817 public nothrow ulong ParseOctal(const string& s)
818 {
819 ulong x = 0u;
820 if (ParseOctal(s, x))
821 {
822 return x;
823 }
824 else
825 {
826 ThrowConversionException("cannot parse octal from string '" + s + "'");
827 }
828 return 0u;
829 }
830
831 public nothrow bool ParseDate(const string& dateStr, Date& date)
832 {
833 int dateEnd = 0;
834 return ParseDate(dateStr, date, dateEnd);
835 }
836
837 public nothrow bool ParseDate(const string& dateStr, Date& date, int& dateEnd)
838 {
839 short year;
840 if (dateStr.Length() < 4 + 2 + 2)
841 {
842 return false;
843 }
844 if (!ParseShort(dateStr.Substring(0, 4), year))
845 {
846 return false;
847 }
848 int monthStart = 4;
849 if (dateStr[4] == '-')
850 {
851 ++monthStart;
852 }
853 if (dateStr.Length() < monthStart + 2)
854 {
855 return false;
856 }
857 sbyte month;
858 if (!ParseSByte(dateStr.Substring(monthStart, 2), month))
859 {
860 return false;
861 }
862 if (month < 1 || month > 12)
863 {
864 return false;
865 }
866 int dayStart = monthStart + 2;
867 if (dateStr[dayStart] == '-')
868 {
869 ++dayStart;
870 }
871 if (dateStr.Length() < dayStart + 2)
872 {
873 return false;
874 }
875 sbyte day;
876 if (!ParseSByte(dateStr.Substring(dayStart, 2), day))
877 {
878 return false;
879 }
880 if (day < 1 || day > 31)
881 {
882 return false;
883 }
884 dateEnd = dayStart + 2;
885 date = Date(year, cast<Month>(month), day);
886 return true;
887 }
888
889 public Date ParseDate(const string& s)
890 {
891 Date date;
892 if (!ParseDate(s, date))
893 {
894 ThrowConversionException("cannot parse date from string '" + s + "': not in format YYYY-MM-DD or YYYYMMDD");
895 }
896 return date;
897 }
898
899 public bool ParseDateTime(const string& dateTimeStr, DateTime& dateTime)
900 {
901 Date date;
902 int dateEnd = 0;
903 if (!ParseDate(dateTimeStr, date, dateEnd))
904 {
905 return false;
906 }
907 int hours = 0;
908 int mins = 0;
909 int secs = 0;
910 if (dateTimeStr.Length() > dateEnd)
911 {
912 if (dateTimeStr[dateEnd] == 'T')
913 {
914 int hoursStart = dateEnd + 1;
915 if (!ParseInt(dateTimeStr.Substring(hoursStart, 2), hours))
916 {
917 return false;
918 }
919 if (hours < 0 || hours > 24)
920 {
921 return false;
922 }
923 if (dateTimeStr.Length() > hoursStart + 2)
924 {
925 int minsStart = hoursStart + 2;
926 if (dateTimeStr[minsStart] == ':')
927 {
928 ++minsStart;
929 }
930 if (!ParseInt(dateTimeStr.Substring(minsStart, 2), mins))
931 {
932 return false;
933 }
934 if (mins < 0 || mins >= 60)
935 {
936 return false;
937 }
938 if (dateTimeStr.Length() > minsStart + 2)
939 {
940 int secsStart = minsStart + 2;
941 if (dateTimeStr[secsStart] == ':')
942 {
943 ++secsStart;
944 }
945 if (!ParseInt(dateTimeStr.Substring(secsStart, 2), secs))
946 {
947 return false;
948 }
949 if (secs < 0 || secs > 60)
950 {
951 return false;
952 }
953 }
954 }
955 }
956 }
957 int totalSecs = hours * 3600 + mins * 60 + secs;
958 dateTime = DateTime(date, totalSecs);
959 return true;
960 }
961
962 public DateTime ParseDateTime(const string& s)
963 {
964 DateTime dateTime;
965 if (!ParseDateTime(s, dateTime))
966 {
967 ThrowConversionException("cannot parse date time from string '" + s + "': not in format YYYY[-]MM[-]DD or YYYY[-]MM[-]DDTHH[[:]MM[[:]SS]]");
968 }
969 return dateTime;
970 }
971
972 public nothrow bool ParseTimestamp(const string& s, Timestamp& timestamp)
973 {
974 if (s.Length() != 20 + 9)
975 {
976 return false;
977 }
978 DateTime dateTime;
979 if (!ParseDateTime(s.Substring(0, 19), dateTime))
980 {
981 return false;
982 }
983 int nanosecs = 0;
984 if (!ParseInt(s.Substring(20), nanosecs))
985 {
986 return false;
987 }
988 timestamp = Timestamp(dateTime, nanosecs);
989 return true;
990 }
991
992 public Timestamp ParseTimestamp(const string& s)
993 {
994 if (s.Length() != 20 + 9)
995 {
996 ThrowConversionException("cannot parse timestamp from string '" + s + "': not in format YYYY-MM-DDTHH:MM:SS.nnnnnnnnn");
997 }
998 DateTime dateTime = ParseDateTime(s.Substring(0, 19));
999 int nanosecs = ParseInt(s.Substring(20));
1000 return Timestamp(dateTime, nanosecs);
1001 }
1002 }