1 /// 2 module mir.format_impl; 3 4 import mir.format; 5 6 @safe pure @nogc nothrow: 7 8 9 size_t printFloatingPointExtend(T, C)(T c, scope ref const FormatSpec spec, scope ref C[512] buf) @trusted 10 { 11 char[512] cbuf = void; 12 return extendASCII(cbuf[].ptr, buf[].ptr, printFloatingPoint(cast(double)c, spec, cbuf)); 13 } 14 15 size_t printFloatingPointGen(T)(T c, scope ref const FormatSpec spec, scope ref char[512] buf) @trusted 16 if(is(T == float) || is(T == double) || is(T == real)) 17 { 18 import mir.math.common: copysign, fabs; 19 bool neg = copysign(1, c) < 0; 20 c = fabs(c); 21 char specFormat = spec.format; 22 version (CRuntime_Microsoft) 23 { 24 if (c != c || c.fabs == c.infinity) 25 { 26 size_t i; 27 char s = void; 28 if (copysign(1, c) < 0) 29 s = '-'; 30 else 31 if (spec.plus) 32 s = '+'; 33 else 34 if (spec.space) 35 s = ' '; 36 else 37 goto S; 38 buf[0] = s; 39 i = 1; 40 S: 41 static immutable char[3][2][2] special = [["inf", "INF"], ["nan", "NAN"]]; 42 auto p = &special[c != c][(specFormat & 0xDF) == specFormat][0]; 43 buf[i + 0] = p[0]; 44 buf[i + 1] = p[1]; 45 buf[i + 2] = p[2]; 46 return i + 3; 47 } 48 } 49 alias T = double; 50 static if (is(T == real)) 51 align(4) char[12] fmt = "%%%%%%*.*gL\0"; 52 else 53 align(4) char[12] fmt = "%%%%%%*.*g\0\0"; 54 55 if (specFormat && specFormat != 's' && specFormat != 'g' && specFormat != 'G') 56 { 57 assert ( 58 specFormat == 'e' 59 || specFormat == 'E' 60 || specFormat == 'f' 61 || specFormat == 'F' 62 || specFormat == 'a' 63 || specFormat == 'A', "Wrong floating point format specifier."); 64 fmt[9] = specFormat; 65 } 66 uint fmtRevLen = 5; 67 if (spec.hash) fmt[fmtRevLen--] = '#'; 68 if (spec.space) fmt[fmtRevLen--] = ' '; 69 if (spec.zero) fmt[fmtRevLen--] = '0'; 70 if (spec.plus) fmt[fmtRevLen--] = '+'; 71 if (spec.dash) fmt[fmtRevLen--] = '-'; 72 73 import core.stdc.stdio : snprintf; 74 ptrdiff_t res = assumePureSafe(&snprintf)((()@trusted =>buf.ptr)(), buf.length - 1, &fmt[fmtRevLen], spec.width, spec.precision, c); 75 assert (res >= 0, "snprintf failed to print a floating point number"); 76 import mir.utility: min; 77 return res < 0 ? 0 : min(cast(size_t)res, buf.length - 1); 78 } 79 80 auto assumePureSafe(T)(T t) @trusted 81 // if (isFunctionPointer!T || isDelegate!T) 82 { 83 import std.traits; 84 enum attrs = (functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.safe) & ~FunctionAttribute.system; 85 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 86 } 87 88 ////////// FLOATING POINT ////////// 89 90 size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref char[512] buf) 91 { 92 return printFloatingPoint(cast(double)c, spec, buf); 93 } 94 95 size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref char[512] buf) 96 { 97 return printFloatingPointGen(c, spec, buf); 98 } 99 100 size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref char[512] buf) 101 { 102 version (CRuntime_Microsoft) 103 { 104 return printFloatingPoint(cast(double) c, spec, buf); 105 } 106 else 107 { 108 return printFloatingPointGen(c, spec, buf); 109 } 110 } 111 112 size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref wchar[512] buf) 113 { 114 return printFloatingPoint(cast(double)c, spec, buf); 115 } 116 117 size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref wchar[512] buf) 118 { 119 return printFloatingPointExtend(c, spec, buf); 120 } 121 122 size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref wchar[512] buf) 123 { 124 version (CRuntime_Microsoft) 125 { 126 return printFloatingPoint(cast(double) c, spec, buf); 127 } 128 else 129 { 130 return printFloatingPointExtend(c, spec, buf); 131 } 132 } 133 134 size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref dchar[512] buf) 135 { 136 return printFloatingPoint(cast(double)c, spec, buf); 137 } 138 139 size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref dchar[512] buf) 140 { 141 return printFloatingPointExtend(c, spec, buf); 142 } 143 144 size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref dchar[512] buf) 145 { 146 version (CRuntime_Microsoft) 147 { 148 return printFloatingPoint(cast(double) c, spec, buf); 149 } 150 else 151 { 152 return printFloatingPointExtend(c, spec, buf); 153 } 154 } 155 156 nothrow: 157 158 size_t printHexadecimal(uint c, ref char[8] buf, bool upper) { return printHexadecimalGen!(uint, char)(c, buf, upper); } 159 size_t printHexadecimal(ulong c, ref char[16] buf, bool upper) { return printHexadecimalGen!(ulong, char)(c, buf, upper); } 160 static if (is(ucent)) 161 size_t printHexadecimal(ucent c, ref char[32] buf, bool upper) { return printHexadecimalGen!(ucent, char)(c, buf, upper); } 162 163 size_t printHexadecimal(uint c, ref wchar[8] buf, bool upper) { return printHexadecimalGen!(uint, wchar)(c, buf, upper); } 164 size_t printHexadecimal(ulong c, ref wchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, wchar)(c, buf, upper); } 165 static if (is(ucent)) 166 size_t printHexadecimal(ucent c, ref wchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, wchar)(c, buf, upper); } 167 168 size_t printHexadecimal(uint c, ref dchar[8] buf, bool upper) { return printHexadecimalGen!(uint, dchar)(c, buf, upper); } 169 size_t printHexadecimal(ulong c, ref dchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, dchar)(c, buf, upper); } 170 static if (is(ucent)) 171 size_t printHexadecimal(ucent c, ref dchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, dchar)(c, buf, upper); } 172 173 size_t printHexadecimalGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) @trusted 174 { 175 if (c < 10) 176 { 177 buf[0] = cast(char)('0' + c); 178 return 1; 179 } 180 import mir.bitop: ctlz; 181 immutable hexString = upper ? hexStringUpper : hexStringLower; 182 size_t ret = cast(size_t) ctlz(c); 183 ret = (ret >> 2) + ((ret & 3) != 0); 184 size_t i = ret; 185 do 186 { 187 buf.ptr[--i] = hexStringUpper[c & 0xF]; 188 c >>= 4; 189 } 190 while(i); 191 return ret; 192 } 193 194 size_t printHexAddress(ubyte c, ref char[2] buf, bool upper) { return printHexAddressGen!(ubyte, char)(c, buf, upper); } 195 size_t printHexAddress(ushort c, ref char[4] buf, bool upper) { return printHexAddressGen!(ushort, char)(c, buf, upper); } 196 size_t printHexAddress(uint c, ref char[8] buf, bool upper) { return printHexAddressGen!(uint, char)(c, buf, upper); } 197 size_t printHexAddress(ulong c, ref char[16] buf, bool upper) { return printHexAddressGen!(ulong, char)(c, buf, upper); } 198 static if (is(ucent)) 199 size_t printHexAddress(ucent c, ref char[32] buf, bool upper) { return printHexAddressGen!(ucent, char)(c, buf, upper); } 200 201 size_t printHexAddress(ubyte c, ref wchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, wchar)(c, buf, upper); } 202 size_t printHexAddress(ushort c, ref wchar[4] buf, bool upper) { return printHexAddressGen!(ushort, wchar)(c, buf, upper); } 203 size_t printHexAddress(uint c, ref wchar[8] buf, bool upper) { return printHexAddressGen!(uint, wchar)(c, buf, upper); } 204 size_t printHexAddress(ulong c, ref wchar[16] buf, bool upper) { return printHexAddressGen!(ulong, wchar)(c, buf, upper); } 205 static if (is(ucent)) 206 size_t printHexAddress(ucent c, ref wchar[32] buf, bool upper) { return printHexAddressGen!(ucent, wchar)(c, buf, upper); } 207 208 size_t printHexAddress(ubyte c, ref dchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, dchar)(c, buf, upper); } 209 size_t printHexAddress(ushort c, ref dchar[4] buf, bool upper) { return printHexAddressGen!(ushort, dchar)(c, buf, upper); } 210 size_t printHexAddress(uint c, ref dchar[8] buf, bool upper) { return printHexAddressGen!(uint, dchar)(c, buf, upper); } 211 size_t printHexAddress(ulong c, ref dchar[16] buf, bool upper) { return printHexAddressGen!(ulong, dchar)(c, buf, upper); } 212 static if (is(ucent)) 213 size_t printHexAddress(ucent c, ref dchar[32] buf, bool upper) { return printHexAddressGen!(ucent, dchar)(c, buf, upper); } 214 215 size_t printHexAddressGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) 216 { 217 static if (T.sizeof == 16) 218 { 219 printHexAddress(cast(ulong)(c >> 64), buf[0 .. 16], upper); 220 printHexAddress(cast(ulong) c, buf[16 .. 32], upper); 221 } 222 else 223 { 224 immutable hexString = upper ? hexStringUpper : hexStringLower; 225 foreach_reverse(ref e; buf) 226 { 227 e = hexStringUpper[c & 0xF]; 228 c >>= 4; 229 } 230 } 231 return buf.length; 232 } 233 234 static immutable hexStringUpper = "0123456789ABCDEF"; 235 static immutable hexStringLower = "0123456789abcdef"; 236 237 size_t printBufferShift(size_t length, size_t shift, scope char* ptr) { return printBufferShiftGen!char(length, shift, ptr); } 238 size_t printBufferShift(size_t length, size_t shift, scope wchar* ptr) { return printBufferShiftGen!wchar(length, shift, ptr); } 239 size_t printBufferShift(size_t length, size_t shift, scope dchar* ptr) { return printBufferShiftGen!dchar(length, shift, ptr); } 240 241 size_t printBufferShiftGen(C)(size_t length, size_t shift, scope C* ptr) @trusted 242 { 243 size_t i; 244 do ptr[i] = ptr[shift + i]; 245 while(++i < length); 246 return length; 247 } 248 249 size_t printSigned(int c, scope ref char[11] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 250 size_t printSigned(long c, scope ref char[21] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 251 static if (is(cent)) 252 size_t printSigned(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 253 254 size_t printSigned(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 255 size_t printSigned(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 256 static if (is(cent)) 257 size_t printSigned(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 258 259 size_t printSigned(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 260 size_t printSigned(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 261 static if (is(cent)) 262 size_t printSigned(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 263 264 265 size_t printSignedToTail(int c, scope ref char[11] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 266 size_t printSignedToTail(long c, scope ref char[21] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 267 static if (is(cent)) 268 size_t printSignedToTail(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 269 270 size_t printSignedToTail(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 271 size_t printSignedToTail(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 272 static if (is(cent)) 273 size_t printSignedToTail(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 274 275 size_t printSignedToTail(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 276 size_t printSignedToTail(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 277 static if (is(cent)) 278 size_t printSignedToTail(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 279 280 size_t printSignedGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) @trusted 281 { 282 auto ret = printSignedToTail(c, buf, sign); 283 if (auto shift = buf.length - ret) 284 { 285 return printBufferShift(ret, shift, buf[].ptr); 286 } 287 return ret; 288 } 289 290 size_t printSignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) 291 { 292 if (c < 0) 293 { 294 sign = '-'; 295 c = -c; 296 } 297 298 auto ret = printUnsignedToTail(c, buf[1 .. N]); 299 300 if (sign != '\0') 301 { 302 buf[$ - ++ret] = sign; 303 } 304 return ret; 305 } 306 307 size_t printUnsigned(uint c, scope ref char[10] buf) { return printUnsignedGen(c, buf); } 308 size_t printUnsigned(ulong c, scope ref char[20] buf) { return printUnsignedGen(c, buf); } 309 static if (is(ucent)) 310 size_t printUnsigned(ucent c, scope ref char[39] buf) { return printUnsignedGen(c, buf); } 311 312 size_t printUnsigned(uint c, scope ref wchar[10] buf) { return printUnsignedGen(c, buf); } 313 size_t printUnsigned(ulong c, scope ref wchar[20] buf) { return printUnsignedGen(c, buf); } 314 static if (is(ucent)) 315 size_t printUnsigned(ucent c, scope ref wchar[39] buf) { return printUnsignedGen(c, buf); } 316 317 size_t printUnsigned(uint c, scope ref dchar[10] buf) { return printUnsignedGen(c, buf); } 318 size_t printUnsigned(ulong c, scope ref dchar[20] buf) { return printUnsignedGen(c, buf); } 319 static if (is(ucent)) 320 size_t printUnsigned(ucent c, scope ref dchar[39] buf) { return printUnsignedGen(c, buf); } 321 322 size_t printUnsignedToTail(uint c, scope ref char[10] buf) { return printUnsignedToTailGen(c, buf); } 323 size_t printUnsignedToTail(ulong c, scope ref char[20] buf) { return printUnsignedToTailGen(c, buf); } 324 static if (is(ucent)) 325 size_t printUnsignedToTail(ucent c, scope ref char[39] buf) { return printUnsignedToTailGen(c, buf); } 326 327 size_t printUnsignedToTail(uint c, scope ref wchar[10] buf) { return printUnsignedToTailGen(c, buf); } 328 size_t printUnsignedToTail(ulong c, scope ref wchar[20] buf) { return printUnsignedToTailGen(c, buf); } 329 static if (is(ucent)) 330 size_t printUnsignedToTail(ucent c, scope ref wchar[39] buf) { return printUnsignedToTailGen(c, buf); } 331 332 size_t printUnsignedToTail(uint c, scope ref dchar[10] buf) { return printUnsignedToTailGen(c, buf); } 333 size_t printUnsignedToTail(ulong c, scope ref dchar[20] buf) { return printUnsignedToTailGen(c, buf); } 334 static if (is(ucent)) 335 size_t printUnsignedToTail(ucent c, scope ref dchar[39] buf) { return printUnsignedToTailGen(c, buf); } 336 337 size_t printUnsignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted 338 { 339 static if (T.sizeof == 4) 340 { 341 if (c < 10) 342 { 343 buf[$ - 1] = cast(char)('0' + c); 344 return 1; 345 } 346 static assert(N == 10); 347 } 348 else 349 static if (T.sizeof == 8) 350 { 351 if (c <= uint.max) 352 { 353 return printUnsignedToTail(cast(uint)c, buf[$ - 10 .. $]); 354 } 355 static assert(N == 20); 356 } 357 else 358 static if (T.sizeof == 16) 359 { 360 if (c <= ulong.max) 361 { 362 return printUnsignedToTail(cast(ulong)c, buf[$ - 20 .. $]); 363 } 364 static assert(N == 39); 365 } 366 else 367 static assert(0); 368 size_t refLen = buf.length; 369 do { 370 T nc = c / 10; 371 buf[].ptr[--refLen] = cast(C)('0' + c - nc * 10); 372 c = nc; 373 } 374 while(c); 375 return buf.length - refLen; 376 } 377 378 size_t printUnsignedGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted 379 { 380 auto ret = printUnsignedToTail(c, buf); 381 if (auto shift = buf.length - ret) 382 { 383 return printBufferShift(ret, shift, buf[].ptr); 384 } 385 return ret; 386 } 387 388 nothrow @trusted 389 size_t extendASCII(char* from, wchar* to, size_t n) 390 { 391 foreach (i; 0 .. n) 392 to[i] = from[i]; 393 return n; 394 } 395 396 nothrow @trusted 397 size_t extendASCII(char* from, dchar* to, size_t n) 398 { 399 foreach (i; 0 .. n) 400 to[i] = from[i]; 401 return n; 402 } 403 404 version (mir_test) unittest 405 { 406 import mir.appender; 407 import mir.format; 408 409 assert (stringBuf() << 123L << getData == "123"); 410 static assert (stringBuf() << 123 << getData == "123"); 411 } 412 413 void printIntegralZeroImpl(C, size_t N, W, I)(ref scope W w, I c, size_t zeroLen) 414 { 415 static if (__traits(isUnsigned, I)) 416 alias impl = printUnsignedToTail; 417 else 418 alias impl = printSignedToTail; 419 C[N] buf = void; 420 size_t n = impl(c, buf); 421 static if (!__traits(isUnsigned, I)) 422 { 423 if (c < 0) 424 { 425 n--; 426 w.put(C('-')); 427 } 428 } 429 sizediff_t zeros = zeroLen - n; 430 if (zeros > 0) 431 { 432 do w.put(C('0')); 433 while(--zeros); 434 } 435 w.put(buf[$ - n .. $]); 436 return w; 437 }