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