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