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 }