]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/src/std/internal/cstring.d
Add D front-end, libphobos library, and D2 testsuite.
[thirdparty/gcc.git] / libphobos / src / std / internal / cstring.d
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 version (unittest)
135 {
136 enum buffLength = 16 / To.sizeof; // smaller size to trigger reallocations
137 }
138 else
139 {
140 enum buffLength = 256 / To.sizeof; // production size
141 }
142
143 To[buffLength] _buff; // the 'small string optimization'
144
145 static Res trustedVoidInit() { Res res = void; return res; }
146 }
147
148 Res res = Res.trustedVoidInit(); // expensive to fill _buff[]
149
150 // Note: res._ptr can't point to res._buff as structs are movable.
151
152 To[] p;
153 bool p_is_onstack = true;
154 size_t i;
155
156 static To[] trustedRealloc(To[] buf, size_t i, To[] res, size_t strLength, bool res_is_onstack)
157 @trusted @nogc nothrow
158 {
159 pragma(inline, false); // because it's rarely called
160
161 import core.exception : onOutOfMemoryError;
162 import core.stdc.stdlib : malloc, realloc;
163 import core.stdc.string : memcpy;
164
165 if (res_is_onstack)
166 {
167 size_t newlen = res.length * 3 / 2;
168 if (newlen <= strLength)
169 newlen = strLength + 1; // +1 for terminating 0
170 auto ptr = cast(To*) malloc(newlen * To.sizeof);
171 if (!ptr)
172 onOutOfMemoryError();
173 memcpy(ptr, res.ptr, i * To.sizeof);
174 return ptr[0 .. newlen];
175 }
176 else
177 {
178 if (buf.length >= size_t.max / (2 * To.sizeof))
179 onOutOfMemoryError();
180 const newlen = buf.length * 3 / 2;
181 auto ptr = cast(To*) realloc(buf.ptr, newlen * To.sizeof);
182 if (!ptr)
183 onOutOfMemoryError();
184 return ptr[0 .. newlen];
185 }
186 }
187
188 size_t strLength;
189 static if (hasLength!From)
190 {
191 strLength = str.length;
192 }
193 import std.utf : byUTF;
194 static if (isSomeString!From)
195 {
196 auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF
197 if (r is null) // Bugzilla 14980
198 {
199 res._ptr = null;
200 return res;
201 }
202 }
203 else
204 alias r = str;
205 To[] q = res._buff;
206 foreach (const c; byUTF!(Unqual!To)(r))
207 {
208 if (i + 1 == q.length)
209 {
210 p = trustedRealloc(p, i, res._buff, strLength, p_is_onstack);
211 p_is_onstack = false;
212 q = p;
213 }
214 q[i++] = c;
215 }
216 q[i] = 0;
217 res._length = i;
218 res._ptr = p_is_onstack ? useStack : &p[0];
219 return res;
220 }
221
222 ///
223 nothrow @nogc @system unittest
224 {
225 import core.stdc.string;
226
227 string str = "abc";
228
229 // Intended usage
230 assert(strlen(str.tempCString()) == 3);
231
232 // Correct usage
233 auto tmp = str.tempCString();
234 assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
235
236 // $(RED WARNING): $(RED Incorrect usage)
237 auto pInvalid1 = str.tempCString().ptr;
238 const char* pInvalid2 = str.tempCString();
239 // Both pointers refer to invalid memory here as
240 // returned values aren't assigned to a variable and
241 // both primary expressions are ended.
242 }
243
244 @safe nothrow @nogc unittest
245 {
246 assert("abc".tempCString().asArray == "abc");
247 assert("abc"d.tempCString().ptr.asArray == "abc");
248 assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w);
249
250 import std.utf : byChar, byWchar;
251 char[300] abc = 'a';
252 assert(tempCString(abc[].byChar).buffPtr.asArray == abc);
253 assert(tempCString(abc[].byWchar).buffPtr.asArray == abc);
254 assert(tempCString(abc[].byChar)[] == abc);
255 }
256
257 // Bugzilla 14980
258 nothrow @nogc @safe unittest
259 {
260 const(char[]) str = null;
261 auto res = tempCString(str);
262 const char* ptr = res;
263 assert(ptr is null);
264 }
265
266 version (Windows)
267 alias tempCStringW = tempCString!(wchar, const(char)[]);