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 }