1 /**
2    Analogous to std.conv but @nogc
3  */
4 
5 module nogc.conv;
6 
7 
8 import localimport;
9 import std.experimental.allocator.mallocator: Mallocator;
10 
11 
12 enum BUFFER_SIZE = 1024;
13 
14 @nogc:
15 
16 auto text(size_t bufferSize = BUFFER_SIZE, Allocator = Mallocator, Args...)
17          (scope auto ref Args args)
18     @safe
19 {
20     import automem.vector: StringA;
21     import std.traits: Unqual;
22     import core.stdc.stdio: snprintf;
23 
24     alias String = StringA!Allocator;
25 
26     scope char[bufferSize] buffer = void;
27     String ret;
28 
29     foreach(ref arg; args) {
30         auto ptr = &buffer[0];
31         auto len = buffer.length;
32         auto fmt = format(arg);
33         // @trusted due to appends in `value`
34         auto rawVal = () @trusted { return value!Allocator(arg); }();
35 
36         static if(is(Unqual!(typeof(rawVal)): StringA!Allocator))
37             // @trusted because stringz is @system
38             scope val = () @trusted { return rawVal.stringz; }();
39         else
40             alias val = rawVal;
41 
42         const index = () @trusted { return snprintf(ptr, len, fmt, val); }();
43         auto toAppend = index >= buffer.length - 1
44             ? buffer[0 .. $ - 1]
45             : buffer[0 .. index];
46 
47         () @trusted { ret ~= toAppend; }();
48     }
49 
50     return ret;
51 }
52 
53 private const(char)* format(T)(auto ref const(T) arg) if(is(T == int) || is(T == short) || is(T == byte)) {
54     return &"%d"[0];
55 }
56 
57 private const(char)* format(T)(auto ref const(T) arg) if(is(T == uint) || is(T == ushort) || is(T == ubyte)) {
58     return &"%u"[0];
59 }
60 
61 private const(char)* format(T)(auto ref const(T) arg) if(is(T == long)) {
62     return &"%ld"[0];
63 }
64 
65 private const(char)* format(T)(auto ref const(T) arg) if(is(T == ulong)) {
66     return &"%lu"[0];
67 }
68 
69 private const(char)* format(T)(auto ref const(T) arg) if(is(T == char)) {
70     return &"%c"[0];
71 }
72 
73 private const(char)* format(T)(auto ref const(T) arg) if(is(T == float)) {
74     return &"%f"[0];
75 }
76 
77 private const(char)* format(T)(auto ref const(T) arg) if(is(T == double)) {
78     return &"%lf"[0];
79 }
80 
81 private const(char)* format(T)(auto ref const(T) arg) if(from.std.traits.isPointer!T) {
82     return &"%p"[0];
83 }
84 
85 private const(char)* format(T)(auto ref const(T) arg) if(is(T == string)) {
86     return &"%s"[0];
87 }
88 
89 private const(char)* format(T)(auto ref const(T) arg) if(is(T == void[])) {
90     return &"%s"[0];
91 }
92 
93 private const(char)* format(T)(auto ref const(T) arg)
94     if(is(T == enum) || is(T == bool) || (from.std.range.primitives.isInputRange!T && !is(T == string)) ||
95        from.std.traits.isAssociativeArray!T || from.std.traits.isAggregateType!T)
96 {
97     return &"%s"[0];
98 }
99 
100 
101 private const(char)* format(T)(auto ref const(T) arg)
102     if(is(T == const(void)[]))
103 {
104     return &"%s"[0];
105 }
106 
107 
108 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg)
109     if((from.std.traits.isScalarType!T || from.std.traits.isPointer!T) && !is(T == enum) && !is(T == bool))
110 {
111     return arg;
112 }
113 
114 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg) if(is(T == enum)) {
115     import std.traits: EnumMembers;
116     import std.conv: to;
117 
118     final switch(arg) {
119         static foreach(member; EnumMembers!T) {
120         case member:
121             mixin(`return &"` ~ member.to!string ~ `"[0];`);
122         }
123     }
124 }
125 
126 
127 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg) if(is(T == bool)) {
128     return arg
129         ? &"true"[0]
130         : &"false"[0];
131 }
132 
133 
134 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg) if(is(T == string)) {
135     import automem.vector: StringA;
136     return StringA!Allocator(arg);
137 }
138 
139 private auto value(Allocator = Mallocator, T)(T arg) if(from.std.range.primitives.isInputRange!(from.std.traits.Unqual!T) && !is(T == string)) {
140 
141     import automem.vector: StringA;
142     import std.range: hasLength, isForwardRange, walkLength, save;
143 
144     StringA!Allocator ret;
145 
146     ret ~= "[";
147 
148     static if(hasLength!T)
149         const length = arg.length;
150     else static if(isForwardRange!T)
151         const length = arg.save.walkLength;
152     else
153         const length = size_t.max;
154 
155     size_t i;
156     foreach(elt; arg) {
157         ret ~= text!(BUFFER_SIZE, Allocator)(elt)[];
158         if(++i < length) ret ~= ", ";
159     }
160 
161     ret ~= "]";
162 
163     return ret;
164 }
165 
166 
167 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg) if(from.std.traits.isAssociativeArray!T) {
168 
169     import automem.vector: StringA;
170 
171     StringA!Allocator ret;
172 
173     ret ~= "[";
174 
175     size_t i;
176     foreach(key, val; arg) {
177         ret ~= text!(BUFFER_SIZE, Allocator)(key)[];
178         ret ~= ": ";
179         ret ~= text!(BUFFER_SIZE, Allocator)(val)[];
180         if(++i < arg.length) ret ~= ", ";
181     }
182 
183     ret ~= "]";
184 
185     return ret;
186 }
187 
188 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg)
189     if(from.std.traits.isAggregateType!T && !from.std.range.primitives.isInputRange!T)
190 {
191     import automem.vector: StringA;
192 
193     StringA!Allocator ret;
194 
195     ret ~= T.stringof;
196     ret ~= "(";
197 
198     foreach(i, elt; arg.tupleof) {
199         ret ~= text!(BUFFER_SIZE, Allocator)(elt)[];
200         if(i != arg.tupleof.length - 1) ret ~= ", ";
201     }
202 
203     ret ~= ")";
204 
205     return ret;
206 }
207 
208 
209 private auto value(Allocator = Mallocator, T)(auto ref const(T) arg)
210     if(is(T == void[]))
211 {
212     return &"[void]"[0];
213 }
214 
215 private auto value(Allocator = Mallocator, T)(T arg) @trusted if(is(T == const(void)[])) {
216     return &"[void]"[0];
217 }
218 
219 
220 auto toWStringz(Allocator = Mallocator, T)(in T str) {
221     import automem.vector: Vector;
222     import std.utf: byUTF;
223     import std.traits: isSomeString;
224 
225     Vector!(immutable(wchar), Allocator) ret;
226     () @trusted { ret.reserve(str.length * str[0].sizeof + 1); }();
227 
228     static if(isSomeString!T)
229         alias range = str;
230     else static if(__traits(compiles, str.range))
231         auto range = str.range;
232     else static if(__traits(compiles, str[]))
233         auto range = str[];
234     else
235         static assert(false, "Don't know how to iterate by wchar on `" ~ T.stringof ~ "`");
236 
237     foreach(ch; range.byUTF!wchar)
238         () @trusted { ret ~= ch; }();
239 
240     // null terminator
241     () @trusted { ret ~= 0; }();
242 
243     return ret;
244 }