1 /** 2 Helper functions for working with $(I C strings). 3 4 This module is intended to provide fast, safe and garbage free 5 way to work with $(I C strings). 6 7 Copyright: Denis Shelomovskij 2013-2014 8 9 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 10 11 Authors: Denis Shelomovskij 12 13 Macros: 14 COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, $(D core.$1.$2)) 15 */ 16 module std.internal.cstring; 17 18 /// 19 @safe unittest 20 { 21 version (Posix) 22 { 23 import core.stdc.stdlib : free; 24 import core.sys.posix.stdlib : setenv; 25 import std.exception : enforce; 26 27 void setEnvironment(in char[] name, in char[] value) 28 { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); } 29 } 30 31 version (Windows) 32 { 33 import core.sys.windows.windows : SetEnvironmentVariableW; 34 import std.exception : enforce; 35 36 void setEnvironment(in char[] name, in char[] value) 37 { enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); } 38 } 39 } 40 41 import std.range; 42 import std.traits; 43 44 version (unittest) 45 @property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc @trusted 46 if (isSomeChar!C) 47 in { assert(cstr); } 48 body 49 { 50 size_t length = 0; 51 while (cstr[length]) 52 ++length; 53 return cstr[0 .. length]; 54 } 55 56 /** 57 Creates temporary 0-terminated $(I C string) with copy of passed text. 58 59 Params: 60 To = character type of returned C string 61 str = string or input range to be converted 62 63 Returns: 64 65 The value returned is implicitly convertible to $(D const To*) and 66 has two properties: $(D ptr) to access $(I C string) as $(D const To*) 67 and $(D buffPtr) to access it as $(D To*). 68 69 The value returned can be indexed by [] to access it as an array. 70 71 The temporary $(I C string) is valid unless returned object is destroyed. 72 Thus if returned object is assigned to a variable the temporary is 73 valid unless the variable goes out of scope. If returned object isn't 74 assigned to a variable it will be destroyed at the end of creating 75 primary expression. 76 77 Implementation_note: 78 For small strings tempCString will use stack allocated buffer, 79 for large strings (approximately 250 characters and more) it will 80 allocate temporary one using C's $(D malloc). 81 82 Note: 83 This function is intended to be used in function call expression (like 84 $(D strlen(str.tempCString()))). Incorrect usage of this function may 85 lead to memory corruption. 86 See $(RED WARNING) in $(B Examples) section. 87 */ 88 89 auto tempCString(To = char, From)(From str) 90 if (isSomeChar!To && (isInputRange!From || isSomeString!From) && 91 isSomeChar!(ElementEncodingType!From)) 92 { 93 94 alias CF = Unqual!(ElementEncodingType!From); 95 96 enum To* useStack = () @trusted { return cast(To*) size_t.max; }(); 97 98 static struct Res 99 { 100 @trusted: 101 nothrow @nogc: 102 103 @disable this(); 104 @disable this(this); 105 alias ptr this; 106 107 @property inout(To)* buffPtr() inout pure 108 { 109 return _ptr == useStack ? _buff.ptr : _ptr; 110 } 111 112 @property const(To)* ptr() const pure 113 { 114 return buffPtr; 115 } 116 117 const(To)[] opIndex() const pure 118 { 119 return buffPtr[0 .. _length]; 120 } 121 122 ~this() 123 { 124 if (_ptr != useStack) 125 { 126 import core.stdc.stdlib : free; 127 free(_ptr); 128 } 129 } 130 131 private: 132 To* _ptr; 133 size_t _length; // length of the string 134 135 // the 'small string optimization' 136 version (unittest) 137 { 138 // smaller size to trigger reallocations. Padding is to account for 139 // unittest/non-unittest cross-compilation (to avoid corruption) 140 To[16 / To.sizeof] _buff; 141 To[(256 - 16) / To.sizeof] _unittest_pad; 142 } 143 else 144 { 145 To[256 / To.sizeof] _buff; // production size 146 } 147 148 static Res trustedVoidInit() { Res res = void; return res; } 149 } 150 151 Res res = Res.trustedVoidInit(); // expensive to fill _buff[] 152 153 // Note: res._ptr can't point to res._buff as structs are movable. 154 155 To[] p; 156 bool p_is_onstack = true; 157 size_t i; 158 159 static To[] trustedRealloc(To[] buf, size_t i, To[] res, size_t strLength, bool res_is_onstack) 160 @trusted @nogc nothrow 161 { 162 pragma(inline, false); // because it's rarely called 163 164 import core.exception : onOutOfMemoryError; 165 import core.stdc.stdlib : malloc, realloc; 166 import core.stdc.string : memcpy; 167 168 if (res_is_onstack) 169 { 170 size_t newlen = res.length * 3 / 2; 171 if (newlen <= strLength) 172 newlen = strLength + 1; // +1 for terminating 0 173 auto ptr = cast(To*) malloc(newlen * To.sizeof); 174 if (!ptr) 175 onOutOfMemoryError(); 176 memcpy(ptr, res.ptr, i * To.sizeof); 177 return ptr[0 .. newlen]; 178 } 179 else 180 { 181 if (buf.length >= size_t.max / (2 * To.sizeof)) 182 onOutOfMemoryError(); 183 const newlen = buf.length * 3 / 2; 184 auto ptr = cast(To*) realloc(buf.ptr, newlen * To.sizeof); 185 if (!ptr) 186 onOutOfMemoryError(); 187 return ptr[0 .. newlen]; 188 } 189 } 190 191 size_t strLength; 192 static if (hasLength!From) 193 { 194 strLength = str.length; 195 } 196 import std.utf : byUTF; 197 static if (isSomeString!From) 198 { 199 auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF 200 if (r is null) // Bugzilla 14980 201 { 202 res._ptr = null; 203 return res; 204 } 205 } 206 else 207 alias r = str; 208 To[] q = res._buff; 209 foreach (const c; byUTF!(Unqual!To)(r)) 210 { 211 if (i + 1 == q.length) 212 { 213 p = trustedRealloc(p, i, res._buff, strLength, p_is_onstack); 214 p_is_onstack = false; 215 q = p; 216 } 217 q[i++] = c; 218 } 219 q[i] = 0; 220 res._length = i; 221 res._ptr = p_is_onstack ? useStack : &p[0]; 222 return res; 223 } 224 225 /// 226 nothrow @nogc @system unittest 227 { 228 import core.stdc.string; 229 230 string str = "abc"; 231 232 // Intended usage 233 assert(strlen(str.tempCString()) == 3); 234 235 // Correct usage 236 auto tmp = str.tempCString(); 237 assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr` 238 239 // $(RED WARNING): $(RED Incorrect usage) 240 auto pInvalid1 = str.tempCString().ptr; 241 const char* pInvalid2 = str.tempCString(); 242 // Both pointers refer to invalid memory here as 243 // returned values aren't assigned to a variable and 244 // both primary expressions are ended. 245 } 246 247 @safe nothrow @nogc unittest 248 { 249 assert("abc".tempCString().asArray == "abc"); 250 assert("abc"d.tempCString().ptr.asArray == "abc"); 251 assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w); 252 253 import std.utf : byChar, byWchar; 254 char[300] abc = 'a'; 255 assert(tempCString(abc[].byChar).buffPtr.asArray == abc); 256 assert(tempCString(abc[].byWchar).buffPtr.asArray == abc); 257 assert(tempCString(abc[].byChar)[] == abc); 258 } 259 260 // Bugzilla 14980 261 nothrow @nogc @safe unittest 262 { 263 const(char[]) str = null; 264 auto res = tempCString(str); 265 const char* ptr = res; 266 assert(ptr is null); 267 } 268 269 version (Windows) 270 alias tempCStringW = tempCString!(wchar, const(char)[]); 271