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