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 ref W printIntegralZeroImpl(C, size_t N, W, I)(scope return ref 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 }