1 /** Helper functions for working with $(I C strings).
2 
3 This module is intended to provide fast, safe and garbage free
4 way to work with $(I C strings).
5 
6 Examples:
7 ---
8 version(Posix):
9 
10 import core.stdc.stdlib: free;
11 import core.sys.posix.unistd: getcwd;
12 import core.sys.posix.stdlib: getenv, setenv;
13 import std.exception: enforce;
14 
15 @property string cwd()
16 { return enforce(getcwd(null, 0).moveToString!free()); }
17 
18 string getEnvironment(in char[] name)
19 { return enforce(getenv(name.tempCString()).toString()); }
20 
21 void setEnvironment(in char[] name, in char[] value)
22 { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); }
23 ---
24 ---
25 version(Windows):
26 
27 import core.sys.windows.windows: SetEnvironmentVariableW;
28 import std.exception: enforce;
29 
30 void setEnvironment(in char[] name, in char[] value)
31 { enforce(SetEnvironmentVariableW(name.tempCString!wchar(), value.tempCString!wchar())); }
32 ---
33 
34 Copyright: Denis Shelomovskij 2013
35 
36 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
37 
38 Authors: Denis Shelomovskij
39 
40 Macros:
41 COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, $(D core.$1.$2))
42 */
43 module unstd.c..string;
44 
45 
46 import core.exception;
47 
48 import std.traits;
49 import unstd.utf;
50 version(unittest) import unstd.generictuple;
51 import unstd.memory.allocation;
52 
53 
54 /// Returns $(I C string) length. If $(D cstr) is null returns 0.
55 @property size_t length(C)(in C* cstr) pure nothrow @nogc
56 if(isSomeChar!C)
57 {
58 	if(!cstr)
59 		return 0;
60 	size_t res = 0;
61 	while(cstr[res])
62 		++res;
63 	return res;
64 }
65 
66 pure nothrow @nogc unittest
67 {
68 	assert(!(cast(char*) null).length);
69 	assert(!"".ptr.length);
70 	foreach(s; expressionTuple!("abc", "abc"w, "abc"d))
71 		assert(s.ptr.length == 3);
72 }
73 
74 
75 /**
76 Compare $(I C strings) lexicographically.
77 
78 Preconditions:
79 $(D cstr1 != null && cstr2 != null)
80 */
81 @property int cmpCStrings(C)(in C* cstr1, in C* cstr2) pure nothrow @nogc
82 if(isSomeChar!C)
83 in { assert(cstr1 && cstr2); }
84 body
85 {
86 	if(cstr1 == cstr2)
87 		return 0;
88 	for(size_t i = 0; ; ++i)
89 	{
90 		const c1 = cstr1[i], c2 = cstr2[i];
91 		if(!c1 && !c2)
92 			return 0;
93 		if(c1 != c2)
94 			return c1 > c2 ? 1 : -1;
95 	}
96 }
97 
98 ///
99 pure nothrow @nogc unittest
100 {
101 	assert(cmpCStrings("ab".ptr, "ab".ptr) == 0);
102 	assert(cmpCStrings("ab".ptr, "abc".ptr) < 0);
103 	assert(cmpCStrings("abc".ptr, "ab".ptr) > 0);
104 }
105 
106 pure nothrow unittest
107 {
108 	string prev = null;
109 	foreach(s; ["", "a", "abc", "abcd", "я"])
110 	{
111 		assert(cmpCStrings(s.ptr, s.ptr) == 0);
112 		assert(cmpCStrings(s.ptr, (s ~ '\0').dup.ptr) == 0);
113 		if(prev)
114 		{
115 			assert(cmpCStrings(prev.ptr, s.ptr) == -1);
116 			assert(cmpCStrings(s.ptr, prev.ptr) == 1);
117 		}
118 		prev = s;
119 	}
120 }
121 
122 
123 /**
124 Returns whether two $(I C strings) are equal.
125 
126 Preconditions:
127 $(D cstr1 != null && cstr2 != null)
128 */
129 @property bool equalCStrings(C)(in C* cstr1, in C* cstr2) pure nothrow @nogc
130 if(isSomeChar!C)
131 in { assert(cstr1 && cstr2); }
132 body
133 {
134 	if(cstr1 == cstr2)
135 		return true;
136 	for(size_t i = 0; ; ++i)
137 	{
138 		const c1 = cstr1[i];
139 		if(c1 != cstr2[i])
140 			return false;
141 		if(!c1)
142 			return true;
143 	}
144 }
145 
146 ///
147 pure nothrow @nogc unittest
148 {
149 	assert( equalCStrings("ab".ptr, "ab".ptr));
150 	assert(!equalCStrings("ab".ptr, "abc".ptr));
151 	assert( equalCStrings("ab".ptr, "ab\0cd".ptr));
152 }
153 
154 
155 /**
156 Returns array representing $(I C string) where $(D '\0') character is placed
157 after the end of the array. If $(D cstr) is null returns null.
158 */
159 @property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc
160 if(isSomeChar!C)
161 {
162 	if(!cstr)
163 		return null;
164 
165 	return cstr[0 .. cstr.length];
166 }
167 
168 pure nothrow @nogc unittest
169 {
170 	foreach(s; expressionTuple!(cast(char[]) null, "", "abc", "abc"w, "abc"d))
171 	{
172 		auto var = s; // Have to assign to variable first.
173 		assert(var.ptr.asArray is var);
174 	}
175 }
176 
177 
178 /**
179 Creates GC-allocated $(D string) with copy of $(I C string) text.
180 If $(D cstr) is null returns null, otherwise if $(D cstr) is empty
181 returns $(D "").
182 */
183 string toString(C)(in C* cstr) pure
184 if(isSomeChar!C)
185 {
186 	if(!cstr)
187 		return null;
188 	if(!*cstr)
189 		return "";
190 	const arr = cstr.asArray;
191 	static if(is(C : char))
192 		return arr.idup;
193 	else
194 		return arr.toUTF8();
195 }
196 
197 pure unittest
198 {
199 	foreach(s; expressionTuple!(cast(char[]) null, "", "abc", "abc"w, "abc"d))
200 		assert(s.ptr.toString() == s.toUTF8());
201 }
202 
203 
204 /**
205 Creates $(I C string) allocated using $(D tryAllocate) with copy of $(D str).
206 If $(D str) is null returns null.
207 
208 $(D tryAllocate) is assumed to return properly aligned for $(D To) memory or
209 null if allocation fails.
210 
211 If allocation fails toCString will call $(COREREF exception, onOutOfMemoryError)
212 which is expected to throw an $(COREREF exception, OutOfMemoryError).
213 */
214 To* toCString(alias tryAllocate, To = char, From)(in From[] str)
215 if(isSomeChar!To && isSomeChar!From)
216 {
217 	if(!str)
218 		return null;
219 
220 	const maxLen = maxLength!To(str);
221 	To* cstr = null;
222 	if(const totalCount = memoryAdd(maxLen, 1))
223 		if(const totalBytes = memoryMult(To.sizeof, totalCount))
224 			cstr = cast(To*) tryAllocate(totalBytes);
225 	if(!cstr)
226 		onOutOfMemoryError();
227 	To[] carr = copyEncoded(str, cstr[0 .. maxLen]);
228 	*(cstr + carr.length) = '\0';
229 	return cstr;
230 }
231 
232 ///
233 nothrow @nogc unittest
234 {
235 	import core.stdc.stdlib;
236 	import core.stdc..string;
237 
238 	string str = "abc";
239 
240 	char* cstr = str.toCString!malloc();
241 	scope(exit) free(cstr);
242 	assert(strlen(cstr) == 3);
243 }
244 
245 unittest
246 {
247 	import core.stdc.stdlib;
248 
249 	assert("abc".toCString!malloc().moveToString!free() == "abc");
250 	assert("abc"d.toCString!malloc().moveToString!free() == "abc");
251 	assert("abc".toCString!(malloc, wchar)().moveToString!free() == "abc");
252 }
253 
254 
255 /**
256 Returns same as $(MREF toString) but also if $(D cstr) is not null
257 releases its memory calling $(D release(cast(void*) cstr)).
258 */
259 string moveToString(alias release, C)(C* cstr)
260 if(isSomeChar!C)
261 {
262 	scope(exit) if(cstr) release(cast(void*) cstr);
263 	return cstr.toString();
264 }
265 
266 unittest
267 {
268 	import core.stdc.stdlib;
269 
270 	auto cstr = cast(char*) malloc(4);
271 	assert(cstr);
272 	cstr[0 .. 4] = "abc\0";
273 	assert(cstr.moveToString!free() == "abc");
274 }
275 
276 
277 /**
278 Creates temporary $(I C string) with copy of passed text.
279 
280 Returned object is implicitly convertible to $(D const To*) and
281 has two properties: $(D ptr) to access $(I C string) as $(D const To*)
282 and $(D buffPtr) to access it as $(D To*).
283 
284 The temporary $(I C string) is valid unless returned object is destroyed.
285 Thus if returned object is assigned to a variable the temporary is
286 valid unless the variable goes out of scope. If returned object isn't
287 assigned to a variable it will be destroyed at the end of creating
288 primary expression.
289 
290 Implementation_note:
291 For small strings tempCString will use stack allocated buffer,
292 for large strings (approximately 1000 characters and more) it will
293 allocate temporary one from $(DPREF2 memory, allocation, threadHeap).
294 
295 Note:
296 This function is intended to be used in function call expression (like
297 $(D strlen(str.tempCString()))). Incorrect usage of this function may
298 lead to memory corruption.
299 See $(RED WARNING) in $(B Examples) section.
300 
301 See_Also:
302 $(DPREF2 memory, allocation, tempAlloc)
303 */
304 auto tempCString(To = char, From)(in From[] str)
305 if(isSomeChar!To && isSomeChar!From)
306 {
307 	enum useStack = cast(To*) -1;
308 
309 	static struct Res
310 	{
311 		@disable this();
312 		@disable this(this);
313 		alias ptr this;
314 
315 		@property inout(To)* buffPtr() inout @safe pure nothrow @nogc
316 		{ return _ptr == useStack ? _buff.ptr : _ptr; }
317 
318 		@property const(To)* ptr() const @safe pure nothrow @nogc
319 		{ return buffPtr; }
320 
321 		~this()
322 		{ if(_ptr != useStack) threadHeap.free(_ptr); }
323 
324 	private:
325 		To* _ptr;
326 		To[1024] _buff;
327 	}
328 
329 	// TODO: Don't stack allocate uninitialized array to
330 	// not confuse unprecise GC.
331 
332 	Res res = void;
333 	if(!str)
334 	{
335 		res._ptr = null;
336 		return res;
337 	}
338 
339 	// Note: res._ptr can't point to res._buff as structs are movable.
340 
341 	const totalCount = memoryAdd(maxLength!To(str), 1);
342 	if(!totalCount)
343 		onOutOfMemoryError();
344 	const needAllocate = totalCount > res._buff.length;
345 	To[] arr = copyEncoded(str, needAllocate ?
346 		threadHeap.allocate!To(totalCount)[0 .. $ - 1] : res._buff[0 .. totalCount - 1]);
347 	*(arr.ptr + arr.length) = '\0';
348 	res._ptr = needAllocate ? arr.ptr : useStack;
349 	return res;
350 }
351 
352 ///
353 unittest
354 {
355 	import core.stdc..string;
356 
357 	string str = "abc";
358 
359 	// Intended usage
360 	assert(strlen(str.tempCString()) == 3);
361 
362 	// Correct usage
363 	auto tmp = str.tempCString();
364 	assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
365 
366 	// $(RED WARNING): $(RED Incorrect usage)
367 	auto pInvalid1 = str.tempCString().ptr;
368 	const char* pInvalid2 = str.tempCString();
369 	// Both pointers refer to invalid memory here as
370 	// returned values aren't assigned to a variable and
371 	// both primary expressions are ended.
372 }
373 
374 unittest
375 {
376 	assert("abc".tempCString().asArray == "abc");
377 	assert("abc"d.tempCString().ptr.asArray == "abc");
378 	assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w);
379 }