xref: /netbsd-src/external/gpl3/gcc.old/dist/libphobos/src/std/internal/cstring.d (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
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