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 T[] 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     wchar[65] wbuf = void;
194     assert(1.signedToTempString(wbuf) == "1"w);
195 
196     dchar[65] dbuf = void;
197     assert(1.signedToTempString(dbuf) == "1"d);
198 
199     // use stack allocated struct version
200     assert(0.signedToTempString() == "0");
201     assert(1.signedToTempString == "1");
202     assert((-1).signedToTempString == "-1");
203     assert(12.signedToTempString == "12");
204     assert((-12).signedToTempString == "-12");
205     assert(0x12ABCF .signedToTempString!16 == "12abcf");
206     assert((-0x12ABCF) .signedToTempString!16 == "-12abcf");
207     assert(long.sizeof.signedToTempString == "8");
208     assert(int.max.signedToTempString == "2147483647");
209     assert(int.min.signedToTempString == "-2147483648");
210     assert(long.max.signedToTempString == "9223372036854775807");
211     assert(long.min.signedToTempString == "-9223372036854775808");
212     assert(long.max.signedToTempString!2 == "111111111111111111111111111111111111111111111111111111111111111");
213     assert(long.min.signedToTempString!2 == "-1000000000000000000000000000000000000000000000000000000000000000");
214 }
215 
216 
217 /********************************
218  * Determine number of digits that will result from a
219  * conversion of value to a string.
220  * Params:
221  *      value = number to convert
222  *      radix = radix
223  * Returns:
224  *      number of digits
225  */
226 int numDigits(uint radix = 10)(ulong value) if (radix >= 2 && radix <= 36)
227 {
228      int n = 1;
229      while (1)
230      {
231         if (value <= uint.max)
232         {
233             uint v = cast(uint)value;
234             while (1)
235             {
236                 if (v < radix)
237                     return n;
238                 if (v < radix * radix)
239                     return n + 1;
240                 if (v < radix * radix * radix)
241                     return n + 2;
242                 if (v < radix * radix * radix * radix)
243                     return n + 3;
244                 n += 4;
245                 v /= radix * radix * radix * radix;
246             }
247         }
248         n += 4;
249         value /= radix * radix * radix * radix;
250      }
251 }
252 
253 unittest
254 {
255     assert(0.numDigits == 1);
256     assert(9.numDigits == 1);
257     assert(10.numDigits == 2);
258     assert(99.numDigits == 2);
259     assert(100.numDigits == 3);
260     assert(999.numDigits == 3);
261     assert(1000.numDigits == 4);
262     assert(9999.numDigits == 4);
263     assert(10000.numDigits == 5);
264     assert(99999.numDigits == 5);
265     assert(uint.max.numDigits == 10);
266     assert(ulong.max.numDigits == 20);
267 
268     assert(0.numDigits!2 == 1);
269     assert(1.numDigits!2 == 1);
270     assert(2.numDigits!2 == 2);
271     assert(3.numDigits!2 == 2);
272 
273     // test bad radices
274     static assert(!__traits(compiles, 100.numDigits!1()));
275     static assert(!__traits(compiles, 100.numDigits!0()));
276     static assert(!__traits(compiles, 100.numDigits!37()));
277 }
278 
279 int dstrcmp()( scope const char[] s1, scope const char[] s2 ) @trusted
280 {
281     immutable len = s1.length <= s2.length ? s1.length : s2.length;
282     if (__ctfe)
283     {
284         foreach (const u; 0 .. len)
285         {
286             if (s1[u] != s2[u])
287                 return s1[u] > s2[u] ? 1 : -1;
288         }
289     }
290     else
291     {
292         import core.stdc.string : memcmp;
293 
294         const ret = memcmp( s1.ptr, s2.ptr, len );
295         if ( ret )
296             return ret;
297     }
298     return (s1.length > s2.length) - (s1.length < s2.length);
299 }