1 /**
2 * String manipulation and comparison utilities.
3 *
4 * Copyright: Copyright Sean Kelly 2005 - 2009.
5 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors: Sean Kelly, Walter Bright
7 * Source: $(DRUNTIMESRC rt/util/_string.d)
8 */
9
10 module core.internal.string;
11
12 pure:
13 nothrow:
14 @nogc:
15
16 alias UnsignedStringBuf = char[20];
17
18 /**
19 Converts an unsigned integer value to a string of characters.
20
21 This implementation is a template so it can be used when compiling with -betterC.
22
23 Params:
24 value = the unsigned integer value to convert
25 buf = the pre-allocated buffer used to store the result
26 radix = the numeric base to use in the conversion (defaults to 10)
27
28 Returns:
29 The unsigned integer value as a string of characters
30 */
31 char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe
32 if (radix >= 2 && radix <= 16)
33 {
34 size_t i = buf.length;
35 do
36 {
37 uint x = void;
38 if (value < radix)
39 {
40 x = cast(uint)value;
41 value = 0;
42 }
43 else
44 {
45 x = cast(uint)(value % radix);
46 value /= radix;
47 }
48 buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a');
49 } while (value);
50 return buf[i .. $];
51 }
52
TempStringNoAlloc(ubyte N)53 private struct TempStringNoAlloc(ubyte N)
54 {
55 private char[N] _buf = void;
56 private ubyte _len;
57 inout(char)[] get() inout return
58 {
59 return _buf[$-_len..$];
60 }
61 alias get this;
62 }
63
64 /**
65 Converts an unsigned integer value to a string of characters.
66
67 This implementation is a template so it can be used when compiling with -betterC.
68
69 Params:
70 value = the unsigned integer value to convert
71 radix = the numeric base to use in the conversion (defaults to 10)
72
73 Returns:
74 The unsigned integer value as a string of characters
75 */
76 auto unsignedToTempString(uint radix = 10)(ulong value) @safe
77 {
78 // Need a buffer of 65 bytes for radix of 2 with room for
79 // signedToTempString to possibly add a negative sign.
80 enum bufferSize = radix >= 10 ? 20 : 65;
81 TempStringNoAlloc!bufferSize result = void;
82 result._len = unsignedToTempString!radix(value, result._buf).length & 0xff;
83 return result;
84 }
85
86 unittest
87 {
88 UnsignedStringBuf buf;
89 assert(0.unsignedToTempString(buf) == "0");
90 assert(1.unsignedToTempString(buf) == "1");
91 assert(12.unsignedToTempString(buf) == "12");
92 assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf");
93 assert(long.sizeof.unsignedToTempString(buf) == "8");
94 assert(uint.max.unsignedToTempString(buf) == "4294967295");
95 assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615");
96
97 // use stack allocated struct version
98 assert(0.unsignedToTempString == "0");
99 assert(1.unsignedToTempString == "1");
100 assert(12.unsignedToTempString == "12");
101 assert(0x12ABCF .unsignedToTempString!16 == "12abcf");
102 assert(long.sizeof.unsignedToTempString == "8");
103 assert(uint.max.unsignedToTempString == "4294967295");
104 assert(ulong.max.unsignedToTempString == "18446744073709551615");
105
106 // test bad radices
107 assert(!is(typeof(100.unsignedToTempString!1(buf))));
108 assert(!is(typeof(100.unsignedToTempString!0(buf) == "")));
109 }
110
111 alias SignedStringBuf = char[20];
112
113 char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe
114 {
115 bool neg = value < 0;
116 if (neg)
117 value = cast(ulong)-value;
118 auto r = unsignedToTempString!radix(value, buf);
119 if (neg)
120 {
121 // about to do a slice without a bounds check
trustedSlice(return scope char[]r)122 auto trustedSlice(return scope char[] r) @trusted { assert(r.ptr > buf.ptr); return (r.ptr-1)[0..r.length+1]; }
123 r = trustedSlice(r);
124 r[0] = '-';
125 }
126 return r;
127 }
128
129 auto signedToTempString(uint radix = 10)(long value) @safe
130 {
131 bool neg = value < 0;
132 if (neg)
133 value = cast(ulong)-value;
134 auto r = unsignedToTempString!radix(value);
135 if (neg)
136 {
137 r._len++;
138 r.get()[0] = '-';
139 }
140 return r;
141 }
142
143 unittest
144 {
145 SignedStringBuf buf;
146 assert(0.signedToTempString(buf) == "0");
147 assert(1.signedToTempString(buf) == "1");
148 assert((-1).signedToTempString(buf) == "-1");
149 assert(12.signedToTempString(buf) == "12");
150 assert((-12).signedToTempString(buf) == "-12");
151 assert(0x12ABCF .signedToTempString!16(buf) == "12abcf");
152 assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf");
153 assert(long.sizeof.signedToTempString(buf) == "8");
154 assert(int.max.signedToTempString(buf) == "2147483647");
155 assert(int.min.signedToTempString(buf) == "-2147483648");
156 assert(long.max.signedToTempString(buf) == "9223372036854775807");
157 assert(long.min.signedToTempString(buf) == "-9223372036854775808");
158
159 // use stack allocated struct version
160 assert(0.signedToTempString() == "0");
161 assert(1.signedToTempString == "1");
162 assert((-1).signedToTempString == "-1");
163 assert(12.signedToTempString == "12");
164 assert((-12).signedToTempString == "-12");
165 assert(0x12ABCF .signedToTempString!16 == "12abcf");
166 assert((-0x12ABCF) .signedToTempString!16 == "-12abcf");
167 assert(long.sizeof.signedToTempString == "8");
168 assert(int.max.signedToTempString == "2147483647");
169 assert(int.min.signedToTempString == "-2147483648");
170 assert(long.max.signedToTempString == "9223372036854775807");
171 assert(long.min.signedToTempString == "-9223372036854775808");
172 assert(long.max.signedToTempString!2 == "111111111111111111111111111111111111111111111111111111111111111");
173 assert(long.min.signedToTempString!2 == "-1000000000000000000000000000000000000000000000000000000000000000");
174 }
175
176
177 /********************************
178 * Determine number of digits that will result from a
179 * conversion of value to a string.
180 * Params:
181 * value = number to convert
182 * radix = radix
183 * Returns:
184 * number of digits
185 */
186 int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36)
187 {
188 int n = 1;
189 while (1)
190 {
191 if (value <= uint.max)
192 {
193 uint v = cast(uint)value;
194 while (1)
195 {
196 if (v < radix)
197 return n;
198 if (v < radix * radix)
199 return n + 1;
200 if (v < radix * radix * radix)
201 return n + 2;
202 if (v < radix * radix * radix * radix)
203 return n + 3;
204 n += 4;
205 v /= radix * radix * radix * radix;
206 }
207 }
208 n += 4;
209 value /= radix * radix * radix * radix;
210 }
211 }
212
213 unittest
214 {
215 assert(0.numDigits == 1);
216 assert(9.numDigits == 1);
217 assert(10.numDigits == 2);
218 assert(99.numDigits == 2);
219 assert(100.numDigits == 3);
220 assert(999.numDigits == 3);
221 assert(1000.numDigits == 4);
222 assert(9999.numDigits == 4);
223 assert(10000.numDigits == 5);
224 assert(99999.numDigits == 5);
225 assert(uint.max.numDigits == 10);
226 assert(ulong.max.numDigits == 20);
227
228 assert(0.numDigits!2 == 1);
229 assert(1.numDigits!2 == 1);
230 assert(2.numDigits!2 == 2);
231 assert(3.numDigits!2 == 2);
232
233 // test bad radices
234 static assert(!__traits(compiles, 100.numDigits!1()));
235 static assert(!__traits(compiles, 100.numDigits!0()));
236 static assert(!__traits(compiles, 100.numDigits!37()));
237 }
238
dstrcmp()239 int dstrcmp()( scope const char[] s1, scope const char[] s2 ) @trusted
240 {
241 immutable len = s1.length <= s2.length ? s1.length : s2.length;
242 if (__ctfe)
243 {
244 foreach (const u; 0 .. len)
245 {
246 if (s1[u] != s2[u])
247 return s1[u] > s2[u] ? 1 : -1;
248 }
249 }
250 else
251 {
252 import core.stdc.string : memcmp;
253
254 const ret = memcmp( s1.ptr, s2.ptr, len );
255 if ( ret )
256 return ret;
257 }
258 return (s1.length > s2.length) - (s1.length < s2.length);
259 }
260