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 }