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