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 pragma(inline, false) 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 pragma(inline, false) 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 pragma(inline, false) 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 pragma(inline, false); 109 return printFloatingPointGen(c, spec, buf); 110 } 111 } 112 113 pragma(inline, false) size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref wchar[512] buf) 114 { 115 return printFloatingPoint(cast(double)c, spec, buf); 116 } 117 118 pragma(inline, false) size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref wchar[512] buf) 119 { 120 return printFloatingPointExtend(c, spec, buf); 121 } 122 123 pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref wchar[512] buf) 124 { 125 version (CRuntime_Microsoft) 126 { 127 return printFloatingPoint(cast(double) c, spec, buf); 128 } 129 else 130 { 131 return printFloatingPointExtend(c, spec, buf); 132 } 133 } 134 135 pragma(inline, false) size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref dchar[512] buf) 136 { 137 return printFloatingPoint(cast(double)c, spec, buf); 138 } 139 140 pragma(inline, false) size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref dchar[512] buf) 141 { 142 return printFloatingPointExtend(c, spec, buf); 143 } 144 145 pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref dchar[512] buf) 146 { 147 version (CRuntime_Microsoft) 148 { 149 return printFloatingPoint(cast(double) c, spec, buf); 150 } 151 else 152 { 153 return printFloatingPointExtend(c, spec, buf); 154 } 155 } 156 157 nothrow: 158 159 pragma(inline, false) size_t printHexadecimal(uint c, ref char[8] buf, bool upper) { return printHexadecimalGen!(uint, char)(c, buf, upper); } 160 pragma(inline, false) size_t printHexadecimal(ulong c, ref char[16] buf, bool upper) { return printHexadecimalGen!(ulong, char)(c, buf, upper); } 161 static if (is(ucent)) 162 pragma(inline, false) size_t printHexadecimal(ucent c, ref char[32] buf, bool upper) { return printHexadecimalGen!(ucent, char)(c, buf, upper); } 163 164 pragma(inline, false) size_t printHexadecimal(uint c, ref wchar[8] buf, bool upper) { return printHexadecimalGen!(uint, wchar)(c, buf, upper); } 165 pragma(inline, false) size_t printHexadecimal(ulong c, ref wchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, wchar)(c, buf, upper); } 166 static if (is(ucent)) 167 pragma(inline, false) size_t printHexadecimal(ucent c, ref wchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, wchar)(c, buf, upper); } 168 169 pragma(inline, false) size_t printHexadecimal(uint c, ref dchar[8] buf, bool upper) { return printHexadecimalGen!(uint, dchar)(c, buf, upper); } 170 pragma(inline, false) size_t printHexadecimal(ulong c, ref dchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, dchar)(c, buf, upper); } 171 static if (is(ucent)) 172 pragma(inline, false) size_t printHexadecimal(ucent c, ref dchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, dchar)(c, buf, upper); } 173 174 size_t printHexadecimalGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) @trusted 175 { 176 if (c < 10) 177 { 178 buf[0] = cast(char)('0' + c); 179 return 1; 180 } 181 import mir.bitop: ctlz; 182 immutable hexString = upper ? hexStringUpper : hexStringLower; 183 size_t ret = cast(size_t) ctlz(c); 184 ret = (ret >> 2) + ((ret & 3) != 0); 185 size_t i = ret; 186 do 187 { 188 buf.ptr[--i] = hexStringUpper[c & 0xF]; 189 c >>= 4; 190 } 191 while(i); 192 return ret; 193 } 194 195 size_t printHexAddress(ubyte c, ref char[2] buf, bool upper) { return printHexAddressGen!(ubyte, char)(c, buf, upper); } 196 size_t printHexAddress(ushort c, ref char[4] buf, bool upper) { return printHexAddressGen!(ushort, char)(c, buf, upper); } 197 pragma(inline, false) size_t printHexAddress(uint c, ref char[8] buf, bool upper) { return printHexAddressGen!(uint, char)(c, buf, upper); } 198 pragma(inline, false) size_t printHexAddress(ulong c, ref char[16] buf, bool upper) { return printHexAddressGen!(ulong, char)(c, buf, upper); } 199 static if (is(ucent)) 200 pragma(inline, false) size_t printHexAddress(ucent c, ref char[32] buf, bool upper) { return printHexAddressGen!(ucent, char)(c, buf, upper); } 201 202 size_t printHexAddress(ubyte c, ref wchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, wchar)(c, buf, upper); } 203 size_t printHexAddress(ushort c, ref wchar[4] buf, bool upper) { return printHexAddressGen!(ushort, wchar)(c, buf, upper); } 204 pragma(inline, false) size_t printHexAddress(uint c, ref wchar[8] buf, bool upper) { return printHexAddressGen!(uint, wchar)(c, buf, upper); } 205 pragma(inline, false) size_t printHexAddress(ulong c, ref wchar[16] buf, bool upper) { return printHexAddressGen!(ulong, wchar)(c, buf, upper); } 206 static if (is(ucent)) 207 pragma(inline, false) size_t printHexAddress(ucent c, ref wchar[32] buf, bool upper) { return printHexAddressGen!(ucent, wchar)(c, buf, upper); } 208 209 size_t printHexAddress(ubyte c, ref dchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, dchar)(c, buf, upper); } 210 size_t printHexAddress(ushort c, ref dchar[4] buf, bool upper) { return printHexAddressGen!(ushort, dchar)(c, buf, upper); } 211 pragma(inline, false) size_t printHexAddress(uint c, ref dchar[8] buf, bool upper) { return printHexAddressGen!(uint, dchar)(c, buf, upper); } 212 pragma(inline, false) size_t printHexAddress(ulong c, ref dchar[16] buf, bool upper) { return printHexAddressGen!(ulong, dchar)(c, buf, upper); } 213 static if (is(ucent)) 214 pragma(inline, false) size_t printHexAddress(ucent c, ref dchar[32] buf, bool upper) { return printHexAddressGen!(ucent, dchar)(c, buf, upper); } 215 216 size_t printHexAddressGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) 217 { 218 static if (T.sizeof == 16) 219 { 220 printHexAddress(cast(ulong)(c >> 64), buf[0 .. 16], upper); 221 printHexAddress(cast(ulong) c, buf[16 .. 32], upper); 222 } 223 else 224 { 225 immutable hexString = upper ? hexStringUpper : hexStringLower; 226 foreach_reverse(ref e; buf) 227 { 228 e = hexStringUpper[c & 0xF]; 229 c >>= 4; 230 } 231 } 232 return buf.length; 233 } 234 235 static immutable hexStringUpper = "0123456789ABCDEF"; 236 static immutable hexStringLower = "0123456789abcdef"; 237 238 pragma(inline, false) size_t printBufferShift(size_t length, size_t shift, scope char* ptr) { return printBufferShiftGen!char(length, shift, ptr); } 239 pragma(inline, false) size_t printBufferShift(size_t length, size_t shift, scope wchar* ptr) { return printBufferShiftGen!wchar(length, shift, ptr); } 240 pragma(inline, false) size_t printBufferShift(size_t length, size_t shift, scope dchar* ptr) { return printBufferShiftGen!dchar(length, shift, ptr); } 241 242 size_t printBufferShiftGen(C)(size_t length, size_t shift, scope C* ptr) @trusted 243 { 244 size_t i; 245 do ptr[i] = ptr[shift + i]; 246 while(++i < length); 247 return length; 248 } 249 250 pragma(inline, false) size_t printSigned(int c, scope ref char[11] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 251 pragma(inline, false) size_t printSigned(long c, scope ref char[21] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 252 static if (is(cent)) 253 pragma(inline, false) size_t printSigned(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 254 255 pragma(inline, false) size_t printSigned(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 256 pragma(inline, false) size_t printSigned(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 257 static if (is(cent)) 258 pragma(inline, false) size_t printSigned(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 259 260 pragma(inline, false) size_t printSigned(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 261 pragma(inline, false) size_t printSigned(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 262 static if (is(cent)) 263 pragma(inline, false) size_t printSigned(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 264 265 266 pragma(inline, false) size_t printSignedToTail(int c, scope ref char[11] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 267 pragma(inline, false) size_t printSignedToTail(long c, scope ref char[21] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 268 static if (is(cent)) 269 pragma(inline, false) size_t printSignedToTail(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 270 271 pragma(inline, false) size_t printSignedToTail(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 272 pragma(inline, false) size_t printSignedToTail(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 273 static if (is(cent)) 274 pragma(inline, false) size_t printSignedToTail(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 275 276 pragma(inline, false) size_t printSignedToTail(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 277 pragma(inline, false) size_t printSignedToTail(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 278 static if (is(cent)) 279 pragma(inline, false) size_t printSignedToTail(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 280 281 size_t printSignedGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) @trusted 282 { 283 auto ret = printSignedToTail(c, buf, sign); 284 if (auto shift = buf.length - ret) 285 { 286 return printBufferShift(ret, shift, buf[].ptr); 287 } 288 return ret; 289 } 290 291 size_t printSignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) 292 { 293 if (c < 0) 294 { 295 sign = '-'; 296 c = -c; 297 } 298 299 auto ret = printUnsignedToTail(c, buf[1 .. N]); 300 301 if (sign != '\0') 302 { 303 buf[$ - ++ret] = sign; 304 } 305 return ret; 306 } 307 308 pragma(inline, false) size_t printUnsigned(uint c, scope ref char[10] buf) { return printUnsignedGen(c, buf); } 309 pragma(inline, false) size_t printUnsigned(ulong c, scope ref char[20] buf) { return printUnsignedGen(c, buf); } 310 static if (is(ucent)) 311 pragma(inline, false) size_t printUnsigned(ucent c, scope ref char[39] buf) { return printUnsignedGen(c, buf); } 312 313 pragma(inline, false) size_t printUnsigned(uint c, scope ref wchar[10] buf) { return printUnsignedGen(c, buf); } 314 pragma(inline, false) size_t printUnsigned(ulong c, scope ref wchar[20] buf) { return printUnsignedGen(c, buf); } 315 static if (is(ucent)) 316 pragma(inline, false) size_t printUnsigned(ucent c, scope ref wchar[39] buf) { return printUnsignedGen(c, buf); } 317 318 pragma(inline, false) size_t printUnsigned(uint c, scope ref dchar[10] buf) { return printUnsignedGen(c, buf); } 319 pragma(inline, false) size_t printUnsigned(ulong c, scope ref dchar[20] buf) { return printUnsignedGen(c, buf); } 320 static if (is(ucent)) 321 pragma(inline, false) size_t printUnsigned(ucent c, scope ref dchar[39] buf) { return printUnsignedGen(c, buf); } 322 323 pragma(inline, false) size_t printUnsignedToTail(uint c, scope ref char[10] buf) { return printUnsignedToTailGen(c, buf); } 324 pragma(inline, false) size_t printUnsignedToTail(ulong c, scope ref char[20] buf) { return printUnsignedToTailGen(c, buf); } 325 static if (is(ucent)) 326 pragma(inline, false) size_t printUnsignedToTail(ucent c, scope ref char[39] buf) { return printUnsignedToTailGen(c, buf); } 327 328 pragma(inline, false) size_t printUnsignedToTail(uint c, scope ref wchar[10] buf) { return printUnsignedToTailGen(c, buf); } 329 pragma(inline, false) size_t printUnsignedToTail(ulong c, scope ref wchar[20] buf) { return printUnsignedToTailGen(c, buf); } 330 static if (is(ucent)) 331 pragma(inline, false) size_t printUnsignedToTail(ucent c, scope ref wchar[39] buf) { return printUnsignedToTailGen(c, buf); } 332 333 pragma(inline, false) size_t printUnsignedToTail(uint c, scope ref dchar[10] buf) { return printUnsignedToTailGen(c, buf); } 334 pragma(inline, false) size_t printUnsignedToTail(ulong c, scope ref dchar[20] buf) { return printUnsignedToTailGen(c, buf); } 335 static if (is(ucent)) 336 pragma(inline, false) size_t printUnsignedToTail(ucent c, scope ref dchar[39] buf) { return printUnsignedToTailGen(c, buf); } 337 338 size_t printUnsignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted 339 { 340 static if (T.sizeof == 4) 341 { 342 if (c < 10) 343 { 344 buf[$ - 1] = cast(char)('0' + c); 345 return 1; 346 } 347 static assert(N == 10); 348 } 349 else 350 static if (T.sizeof == 8) 351 { 352 if (c <= uint.max) 353 { 354 return printUnsignedToTail(cast(uint)c, buf[$ - 10 .. $]); 355 } 356 static assert(N == 20); 357 } 358 else 359 static if (T.sizeof == 16) 360 { 361 if (c <= ulong.max) 362 { 363 return printUnsignedToTail(cast(ulong)c, buf[$ - 20 .. $]); 364 } 365 static assert(N == 39); 366 } 367 else 368 static assert(0); 369 size_t refLen = buf.length; 370 do { 371 T nc = c / 10; 372 buf[].ptr[--refLen] = cast(C)('0' + c - nc * 10); 373 c = nc; 374 } 375 while(c); 376 return buf.length - refLen; 377 } 378 379 size_t printUnsignedGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted 380 { 381 auto ret = printUnsignedToTail(c, buf); 382 if (auto shift = buf.length - ret) 383 { 384 return printBufferShift(ret, shift, buf[].ptr); 385 } 386 return ret; 387 } 388 389 nothrow @trusted 390 pragma(inline, false) size_t extendASCII(char* from, wchar* to, size_t n) 391 { 392 foreach (i; 0 .. n) 393 to[i] = from[i]; 394 return n; 395 } 396 397 nothrow @trusted 398 pragma(inline, false) size_t extendASCII(char* from, dchar* to, size_t n) 399 { 400 foreach (i; 0 .. n) 401 to[i] = from[i]; 402 return n; 403 } 404 405 unittest 406 { 407 import mir.appender; 408 import mir.format; 409 410 assert (stringBuf() << 123L << getData == "123"); 411 static assert (stringBuf() << 123 << getData == "123"); 412 } 413 414 ref W printIntegralZeroImpl(C, size_t N, W, I)(scope return ref W w, I c, size_t zeroLen) 415 { 416 static if (__traits(isUnsigned, I)) 417 alias impl = printUnsignedToTail; 418 else 419 alias impl = printSignedToTail; 420 C[N] buf = void; 421 size_t n = impl(c, buf); 422 static if (!__traits(isUnsigned, I)) 423 { 424 if (c < 0) 425 { 426 n--; 427 w.put(C('-')); 428 } 429 } 430 sizediff_t zeros = zeroLen - n; 431 if (zeros > 0) 432 { 433 do w.put(C('0')); 434 while(--zeros); 435 } 436 w.put(buf[$ - n .. $]); 437 return w; 438 }