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 }