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
10module core.internal.string;
11
12pure:
13nothrow:
14@nogc:
15
16alias UnsignedStringBuf = char[20];
17
18/**
19Converts an unsigned integer value to a string of characters.
20
21This implementation is a template so it can be used when compiling with -betterC.
22
23Params:
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
28Returns:
29    The unsigned integer value as a string of characters
30*/
31char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe
32if (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
53private 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/**
65Converts an unsigned integer value to a string of characters.
66
67This implementation is a template so it can be used when compiling with -betterC.
68
69Params:
70    value = the unsigned integer value to convert
71    radix = the numeric base to use in the conversion (defaults to 10)
72
73Returns:
74    The unsigned integer value as a string of characters
75*/
76auto 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
86unittest
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
111alias SignedStringBuf = char[20];
112
113char[] 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
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
129auto 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
143unittest
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 */
186int 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
213unittest
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
239int 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