288
|
1 /**
|
|
2 * CTFE Integer routines.
|
|
3 *
|
|
4 * Authors: Daniel Keep <daniel.keep@gmail.com>
|
|
5 * Copyright: See LICENSE.
|
|
6 */
|
344
|
7 module qtd.ctfe.Integer;
|
288
|
8
|
|
9 /**
|
|
10 * Formats an integer as a string. You can optionally specify a different
|
|
11 * base; any value between 2 and 16 inclusive is supported.
|
|
12 *
|
|
13 * Params:
|
|
14 * v = value to format.
|
|
15 * base = base to use; defaults to 10.
|
|
16 * Returns:
|
|
17 * integer formatted as a string.
|
|
18 */
|
|
19
|
|
20 string format_ctfe(intT)(intT v, int base = 10)
|
|
21 {
|
|
22 static if( !is( intT == ulong ) )
|
|
23 {
|
|
24 return (v < 0)
|
|
25 ? "-" ~ format_ctfe(cast(ulong) -v, base)
|
|
26 : format_ctfe(cast(ulong) v, base);
|
|
27 }
|
|
28 else
|
|
29 {
|
|
30 assert( 2 <= base && base <= 16,
|
|
31 "base must be between 2 and 16; got " ~ format_ctfe(base, 10) );
|
|
32
|
|
33 string r = "";
|
|
34 do
|
|
35 {
|
|
36 r = INT_CHARS[cast(size_t)(v % base)] ~ r;
|
|
37 v /= base;
|
|
38 }
|
|
39 while( v > 0 );
|
|
40 return r;
|
|
41 }
|
|
42 }
|
|
43
|
|
44 /**
|
|
45 * Parses an integer value from a string. You can optionally specify a
|
|
46 * different base; any value between 2 and 16 inclusive is supported.
|
|
47 *
|
|
48 * Note that this does not fail if it cannot consume the entire string;
|
|
49 * use returnUsed to determine the number of characters consumed.
|
|
50 *
|
|
51 * Params:
|
|
52 * str = string to parse.
|
|
53 * returnUsed = defaults to false; if set to true, returns the number of
|
|
54 * characters consumed from the string instead of the
|
|
55 * parsed value.
|
|
56 * base = base to use; defaults to 10.
|
|
57 * Returns:
|
|
58 * either the parsed integer or the number of characters consumed,
|
|
59 * depending on the value of returnUsed.
|
|
60 */
|
|
61
|
|
62 intT parse_ctfe(intT)(string str, bool returnUsed = false, int base = 10)
|
|
63 {
|
|
64 auto origStr = str;
|
|
65
|
|
66 assert( 2 <= base && base <= 16,
|
|
67 "base must be between 2 and 16; got " ~ format_ctfe(base, 10) );
|
|
68
|
|
69 bool neg = false;
|
|
70 if( str.length > 0 && str[0] == '-' )
|
|
71 {
|
|
72 neg = true;
|
|
73 str = str[1..$];
|
|
74 }
|
|
75
|
|
76 if( intT.min == 0 && neg )
|
|
77 assert(false, "underwhile while parsing \"" ~ origStr
|
|
78 ~ "\" as a " ~ intT.stringof ~ ": cannot represent negative "
|
|
79 ~ "values");
|
|
80
|
|
81 intT r = 0;
|
|
82 size_t used = 0;
|
|
83
|
|
84 foreach( c ; str )
|
|
85 {
|
|
86 int cv = -1;
|
|
87
|
|
88 if( '0' <= c && c <= '9' )
|
|
89 cv = c - '0';
|
|
90
|
|
91 else if( 'A' <= c && c <= 'Z' )
|
|
92 cv = 10 + c - 'A';
|
|
93
|
|
94 else if( 'a' <= c && c <= 'z' )
|
|
95 cv = 10 + c - 'a';
|
|
96
|
|
97 if( cv >= base || cv < 0 )
|
|
98 break;
|
|
99
|
|
100 auto oldR = r;
|
|
101 r = r*base + cast(intT) cv;
|
|
102 ++ used;
|
|
103
|
|
104 if( r < oldR )
|
|
105 assert(false, "overflow while parsing \"" ~ origStr
|
|
106 ~ "\" as a " ~ intT.stringof);
|
|
107 }
|
|
108
|
|
109 if( neg )
|
|
110 {
|
|
111 r = -r;
|
|
112 ++used;
|
|
113 }
|
|
114
|
|
115 if( returnUsed )
|
|
116 {
|
|
117 assert( used < intT.max, "overflow attempting to return "
|
|
118 ~ "number of characters consumed in a " ~ intT.stringof );
|
|
119
|
|
120 return used;
|
|
121 }
|
|
122 else
|
|
123 return r;
|
|
124 }
|
|
125
|
|
126 /**
|
|
127 * Like parse_ctfe, except it will raise an error if the provided string
|
|
128 * cannot be parsed in its entirety.
|
|
129 *
|
|
130 * Params:
|
|
131 * str = the string to parse.
|
|
132 * base = base to use; defaults to 10.
|
|
133 * Returns:
|
|
134 * the parsed integer.
|
|
135 */
|
|
136
|
|
137 intT parseAll_ctfe(intT)(string str, int base = 10)
|
|
138 {
|
|
139 auto used = parse_ctfe!(intT)(str, true, base);
|
|
140 assert( used == str.length, "could not parse entire string \"" ~ str
|
|
141 ~ "\"" );
|
|
142 return parse_ctfe!(int)(str, false, base);
|
|
143 }
|
|
144
|
|
145 private
|
|
146 {
|
|
147 const INT_CHARS = "0123456789abcdef";
|
|
148 }
|
|
149
|
|
150 version( Unittest )
|
|
151 {
|
|
152 static assert( format_ctfe(0) == "0", "got: " ~ format_ctfe(0) );
|
|
153 static assert( format_ctfe(1) == "1" );
|
|
154 static assert( format_ctfe(-1) == "-1" );
|
|
155 static assert( format_ctfe(42) == "42" );
|
|
156 static assert( format_ctfe(0xf00, 16) == "f00" );
|
|
157 static assert( format_ctfe(0123, 8) == "123" );
|
|
158
|
|
159 static assert( parse_ctfe!(long)("0") == 0 );
|
|
160 static assert( parse_ctfe!(long)("1") == 1 );
|
|
161 static assert( parse_ctfe!(long)("-1") == -1 );
|
|
162 static assert( parse_ctfe!(long)("42") == 42 );
|
|
163 static assert( parse_ctfe!(long)("f00", false, 16) == 0xf00 );
|
|
164 static assert( parse_ctfe!(long)("123", false, 8) == 0123 );
|
|
165 static assert( parse_ctfe!(long)("123ax", true) == 3 );
|
|
166 static assert( parse_ctfe!(long)("123ax", true, 16) == 4 );
|
|
167
|
|
168 static assert( parseAll_ctfe!(long)("123") == 123 );
|
|
169 }
|