1 module decimal.sinks; 2 3 private import std.format: FormatSpec; 4 private import std.traits: isSomeChar, Unqual; 5 6 private import decimal.integrals: prec, divrem, isAnyUnsigned; 7 8 package: 9 10 //dumps value to buffer right aligned, assumes buffer has enough space 11 int dumpUnsigned(C, T)(C[] buffer, auto const ref T value) 12 if (isSomeChar!C && isAnyUnsigned!T) 13 { 14 assert(buffer.length > 0 && buffer.length >= prec(value)); 15 int i = cast(int)buffer.length; 16 Unqual!T v = value; 17 do 18 { 19 auto r = divrem(v, 10U); 20 buffer[--i] = cast(C)(r + cast(uint)'0'); 21 } while (v); 22 return cast(int)buffer.length - i; 23 } 24 25 //dumps value to buffer right aligned, assumes buffer has enough space 26 int dumpUnsignedHex(C, T)(C[] buffer, auto const ref T value, const bool uppercase) 27 if (isSomeChar!C && isAnyUnsigned!T) 28 { 29 assert(buffer.length > 0 && buffer.length >= prec(value)); 30 int i = cast(int)buffer.length; 31 Unqual!T v = value; 32 do 33 { 34 auto digit = (cast(uint)v & 0xFU); 35 buffer[--i] = cast(C)(digit < 10 ? '0' + digit : 36 (uppercase ? 'A' + (digit - 10) : 'a' + (digit - 10))); 37 v >>= 4; 38 } while (v); 39 return cast(int)buffer.length - i; 40 } 41 42 //repeats sinking of value count times using a default buffer size of 8 43 void sinkRepeat(int bufferSize = 8, C)(scope void delegate(const(C)[]) sink, const C value, const int count) 44 if (isSomeChar!C) 45 { 46 if (!count) 47 return; 48 Unqual!C[bufferSize] buffer = value; 49 int cnt = count; 50 while (cnt > 0) 51 { 52 sink(buffer[0 .. cnt > bufferSize ? bufferSize : cnt]); 53 cnt -= bufferSize; 54 } 55 } 56 57 //sinks +/-/space 58 void sinkSign(C)(auto const ref FormatSpec!C spec, scope void delegate(const(C)[]) sink, const bool signed) 59 if (isSomeChar!C) 60 { 61 if (!signed && spec.flPlus) 62 sink("+"); 63 else if (!signed && spec.flSpace) 64 sink(" "); 65 else if (signed) 66 sink("-"); 67 } 68 69 //pads left according to spec 70 void sinkPadLeft(C)(auto const ref FormatSpec!C spec, scope void delegate(const(C)[]) sink, ref int pad) 71 if (isSomeChar!C) 72 { 73 if (pad > 0 && !spec.flDash && !spec.flZero) 74 { 75 sinkRepeat(sink, ' ', pad); 76 pad = 0; 77 } 78 } 79 80 //zero pads left according to spec 81 void sinkPadZero(C)(auto const ref FormatSpec!C spec, scope void delegate(const(C)[]) sink, ref int pad) 82 if (isSomeChar!C) 83 { 84 if (pad > 0 && spec.flZero && !spec.flDash) 85 { 86 sinkRepeat(sink, '0', pad); 87 pad = 0; 88 } 89 } 90 91 //pads right according to spec 92 void sinkPadRight(C)(scope void delegate(const(C)[]) sink, ref int pad) 93 if (isSomeChar!C) 94 { 95 if (pad > 0) 96 { 97 sinkRepeat(sink, ' ', pad); 98 pad = 0; 99 } 100 } 101 102 //sinks +/-(s)nan; 103 void sinkNaN(C, T)(auto const ref FormatSpec!C spec, scope void delegate(const(C)[]) sink, const bool signed, 104 const bool signaling, T payload, bool hex) 105 if (isSomeChar!C) 106 { 107 C[40] buffer; 108 FormatSpec!C nanspec = spec; 109 nanspec.flZero = false; 110 nanspec.flHash = false; 111 ptrdiff_t digits; 112 if (payload) 113 { 114 if (hex) 115 digits = dumpUnsignedHex(buffer, payload, nanspec.spec < 'Z'); 116 else 117 digits = dumpUnsigned(buffer, payload); 118 } 119 int w = signaling ? 4 : 3; 120 if (payload) 121 { 122 if (hex) 123 w += digits + 4; 124 else 125 w += digits + 2; 126 } 127 if (nanspec.flPlus || nanspec.flSpace || signed) 128 ++w; 129 int pad = nanspec.width - w; 130 sinkPadLeft(nanspec, sink, pad); 131 sinkSign(nanspec, sink, signed); 132 if (signaling) 133 sink(nanspec.spec < 'Z' ? "S" : "s"); 134 sink(nanspec.spec < 'Z' ? "NAN" : "nan"); 135 if (payload) 136 { 137 sink("["); 138 if (hex) 139 { 140 sink("0"); 141 sink(nanspec.spec < 'Z' ? "X" : "x"); 142 } 143 sink(buffer[$ - digits .. $ - 1]); 144 sink("]"); 145 } 146 sinkPadRight(sink, pad); 147 } 148 149 //sinks +/-(s)inf; 150 void sinkInfinity(C)(auto const ref FormatSpec!C spec, scope void delegate(const(C)[]) sink, const bool signed) 151 if (isSomeChar!C) 152 { 153 FormatSpec!C infspec = spec; 154 infspec.flZero = false; 155 infspec.flHash = false; 156 157 int w = 3; 158 if (infspec.flPlus || infspec.flSpace || signed) 159 ++w; 160 int pad = infspec.width - w; 161 sinkPadLeft(infspec, sink, pad); 162 sinkSign(infspec, sink, signed); 163 sink(infspec.spec < 'Z' ? "INF" : "inf"); 164 sinkPadRight(sink, pad); 165 } 166 167 //sinks 0 168 void sinkZero(C)(auto const ref FormatSpec!C spec, scope void delegate(const(C)[]) sink, const bool signed, const bool skipTrailingZeros = false) 169 if (isSomeChar!C) 170 { 171 int requestedDecimals = spec.precision == spec.UNSPECIFIED || spec.precision < 0 ? 6 : spec.precision; 172 173 if (skipTrailingZeros) 174 requestedDecimals = 0; 175 176 int w = requestedDecimals == 0 ? 1 : requestedDecimals + 2; 177 178 if (requestedDecimals == 0 && spec.flHash) 179 ++w; 180 181 if (spec.flPlus || spec.flSpace || signed) 182 ++w; 183 int pad = spec.width - w; 184 sinkPadLeft(spec, sink, pad); 185 sinkSign(spec, sink, signed); 186 sinkPadZero(spec, sink, pad); 187 sink("0"); 188 if (requestedDecimals || spec.flHash) 189 { 190 sink("."); 191 sinkRepeat(sink, '0', requestedDecimals); 192 } 193 sinkPadRight(sink, pad); 194 } 195 196