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