xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/src/std/internal/cstring.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1181254a7Smrg /**
2181254a7Smrg Helper functions for working with $(I C strings).
3181254a7Smrg 
4181254a7Smrg This module is intended to provide fast, safe and garbage free
5181254a7Smrg way to work with $(I C strings).
6181254a7Smrg 
7181254a7Smrg Copyright: Denis Shelomovskij 2013-2014
8181254a7Smrg 
9181254a7Smrg License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
10181254a7Smrg 
11181254a7Smrg Authors: Denis Shelomovskij
12181254a7Smrg 
13181254a7Smrg Macros:
14*b1e83836Smrg COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, `core.$1.$2`)
15181254a7Smrg */
16181254a7Smrg module std.internal.cstring;
17181254a7Smrg 
18181254a7Smrg ///
19181254a7Smrg @safe unittest
20181254a7Smrg {
version(Posix)21181254a7Smrg     version (Posix)
22181254a7Smrg     {
23181254a7Smrg         import core.stdc.stdlib : free;
24181254a7Smrg         import core.sys.posix.stdlib : setenv;
25181254a7Smrg         import std.exception : enforce;
26181254a7Smrg 
27*b1e83836Smrg         void setEnvironment(scope const(char)[] name, scope const(char)[] value)
28181254a7Smrg         { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); }
29181254a7Smrg     }
30181254a7Smrg 
version(Windows)31181254a7Smrg     version (Windows)
32181254a7Smrg     {
33*b1e83836Smrg         import core.sys.windows.winbase : SetEnvironmentVariableW;
34181254a7Smrg         import std.exception : enforce;
35181254a7Smrg 
36*b1e83836Smrg         void setEnvironment(scope const(char)[] name, scope const(char)[] value)
37181254a7Smrg         { enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); }
38181254a7Smrg     }
39181254a7Smrg }
40181254a7Smrg 
41181254a7Smrg import std.range;
42181254a7Smrg import std.traits;
43181254a7Smrg 
44181254a7Smrg /**
45181254a7Smrg Creates temporary 0-terminated $(I C string) with copy of passed text.
46181254a7Smrg 
47181254a7Smrg Params:
48181254a7Smrg     To = character type of returned C string
49181254a7Smrg     str = string or input range to be converted
50181254a7Smrg 
51181254a7Smrg Returns:
52181254a7Smrg 
53181254a7Smrg The value returned is implicitly convertible to $(D const To*) and
54*b1e83836Smrg has two properties: `ptr` to access $(I C string) as $(D const To*)
55*b1e83836Smrg and `buffPtr` to access it as `To*`.
56181254a7Smrg 
57181254a7Smrg The value returned can be indexed by [] to access it as an array.
58181254a7Smrg 
59181254a7Smrg The temporary $(I C string) is valid unless returned object is destroyed.
60181254a7Smrg Thus if returned object is assigned to a variable the temporary is
61181254a7Smrg valid unless the variable goes out of scope. If returned object isn't
62181254a7Smrg assigned to a variable it will be destroyed at the end of creating
63181254a7Smrg primary expression.
64181254a7Smrg 
65181254a7Smrg Implementation_note:
66181254a7Smrg For small strings tempCString will use stack allocated buffer,
67181254a7Smrg for large strings (approximately 250 characters and more) it will
68*b1e83836Smrg allocate temporary one using C's `malloc`.
69181254a7Smrg 
70181254a7Smrg Note:
71181254a7Smrg This function is intended to be used in function call expression (like
72*b1e83836Smrg `strlen(str.tempCString())`). Incorrect usage of this function may
73181254a7Smrg lead to memory corruption.
74181254a7Smrg See $(RED WARNING) in $(B Examples) section.
75181254a7Smrg */
76181254a7Smrg 
77*b1e83836Smrg auto tempCString(To = char, From)(scope From str)
78181254a7Smrg if (isSomeChar!To && (isInputRange!From || isSomeString!From) &&
79181254a7Smrg     isSomeChar!(ElementEncodingType!From))
80181254a7Smrg {
81181254a7Smrg     alias CF = Unqual!(ElementEncodingType!From);
82181254a7Smrg 
83*b1e83836Smrg     auto res = TempCStringBuffer!To.trustedVoidInit(); // expensive to fill _buff[]
84181254a7Smrg 
85181254a7Smrg     // Note: res._ptr can't point to res._buff as structs are movable.
86181254a7Smrg 
87*b1e83836Smrg     // https://issues.dlang.org/show_bug.cgi?id=14980
88*b1e83836Smrg     static if (isSomeString!From)
89181254a7Smrg     {
90*b1e83836Smrg         if (str is null)
91181254a7Smrg         {
92*b1e83836Smrg             res._length = 0;
93*b1e83836Smrg             res._ptr = null;
94*b1e83836Smrg             return res;
95*b1e83836Smrg         }
96*b1e83836Smrg     }
97*b1e83836Smrg 
98*b1e83836Smrg     // Use slice assignment if available.
99*b1e83836Smrg     static if (To.sizeof == CF.sizeof && is(typeof(res._buff[0 .. str.length] = str[])))
100*b1e83836Smrg     {
101*b1e83836Smrg         if (str.length < res._buff.length)
102*b1e83836Smrg         {
103*b1e83836Smrg             res._buff[0 .. str.length] = str[];
104*b1e83836Smrg             res._buff[str.length] = 0;
105*b1e83836Smrg             res._ptr = res.useStack;
106181254a7Smrg         }
107181254a7Smrg         else
108181254a7Smrg         {
109*b1e83836Smrg             import std.internal.memory : enforceMalloc;
110*b1e83836Smrg             if (false)
111*b1e83836Smrg             {
112*b1e83836Smrg                 // This code is removed by the compiler but causes `@safe`ty
113*b1e83836Smrg                 // to be inferred correctly.
114*b1e83836Smrg                 CF[0] x;
115*b1e83836Smrg                 x[] = str[0 .. 0];
116181254a7Smrg             }
117*b1e83836Smrg             res._ptr = () @trusted {
118*b1e83836Smrg                 auto p = cast(CF*) enforceMalloc((str.length + 1) * CF.sizeof);
119*b1e83836Smrg                 p[0 .. str.length] = str[];
120*b1e83836Smrg                 p[str.length] = 0;
121*b1e83836Smrg                 return cast(To*) p;
122*b1e83836Smrg             }();
123181254a7Smrg         }
124*b1e83836Smrg         res._length = str.length;
125*b1e83836Smrg         return res;
126*b1e83836Smrg     }
127*b1e83836Smrg     else
128*b1e83836Smrg     {
129*b1e83836Smrg         static assert(!(isSomeString!From && CF.sizeof == To.sizeof), "Should be using slice assignment.");
130*b1e83836Smrg         To[] p = res._buff;
131*b1e83836Smrg         size_t i;
132181254a7Smrg 
133181254a7Smrg         size_t strLength;
134181254a7Smrg         static if (hasLength!From)
135181254a7Smrg         {
136181254a7Smrg             strLength = str.length;
137181254a7Smrg         }
138181254a7Smrg         import std.utf : byUTF;
139181254a7Smrg         static if (isSomeString!From)
140181254a7Smrg             auto r = cast(const(CF)[])str;  // because inout(CF) causes problems with byUTF
141181254a7Smrg         else
142181254a7Smrg             alias r = str;
143*b1e83836Smrg         To[] heapBuffer;
144181254a7Smrg         foreach (const c; byUTF!(Unqual!To)(r))
145181254a7Smrg         {
146*b1e83836Smrg             if (i + 1 == p.length)
147181254a7Smrg             {
148*b1e83836Smrg                 if (heapBuffer is null)
149*b1e83836Smrg                     heapBuffer = trustedReallocStack(p, strLength);
150*b1e83836Smrg                 else
151*b1e83836Smrg                     heapBuffer = trustedRealloc(heapBuffer);
152*b1e83836Smrg                 p = heapBuffer;
153181254a7Smrg             }
154*b1e83836Smrg             p[i++] = c;
155181254a7Smrg         }
156*b1e83836Smrg         p[i] = 0;
157181254a7Smrg         res._length = i;
158*b1e83836Smrg         res._ptr = (heapBuffer is null ? res.useStack : &heapBuffer[0]);
159181254a7Smrg         return res;
160181254a7Smrg     }
161*b1e83836Smrg }
162181254a7Smrg 
163181254a7Smrg ///
164181254a7Smrg nothrow @nogc @system unittest
165181254a7Smrg {
166181254a7Smrg     import core.stdc.string;
167181254a7Smrg 
168181254a7Smrg     string str = "abc";
169181254a7Smrg 
170181254a7Smrg     // Intended usage
171181254a7Smrg     assert(strlen(str.tempCString()) == 3);
172181254a7Smrg 
173181254a7Smrg     // Correct usage
174181254a7Smrg     auto tmp = str.tempCString();
175181254a7Smrg     assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
176181254a7Smrg 
177181254a7Smrg     // $(RED WARNING): $(RED Incorrect usage)
178181254a7Smrg     auto pInvalid1 = str.tempCString().ptr;
179181254a7Smrg     const char* pInvalid2 = str.tempCString();
180181254a7Smrg     // Both pointers refer to invalid memory here as
181181254a7Smrg     // returned values aren't assigned to a variable and
182181254a7Smrg     // both primary expressions are ended.
183181254a7Smrg }
184181254a7Smrg 
185*b1e83836Smrg @safe pure nothrow @nogc unittest
186181254a7Smrg {
inout(C)187*b1e83836Smrg     static inout(C)[] arrayFor(C)(inout(C)* cstr) pure nothrow @nogc @trusted
188*b1e83836Smrg     {
189*b1e83836Smrg         assert(cstr);
190*b1e83836Smrg         size_t length = 0;
191*b1e83836Smrg         while (cstr[length])
192*b1e83836Smrg             ++length;
193*b1e83836Smrg         return cstr[0 .. length];
194*b1e83836Smrg     }
195*b1e83836Smrg 
196*b1e83836Smrg     assert(arrayFor("abc".tempCString()) == "abc");
197*b1e83836Smrg     assert(arrayFor("abc"d.tempCString().ptr) == "abc");
198*b1e83836Smrg     assert(arrayFor("abc".tempCString!wchar().buffPtr) == "abc"w);
199181254a7Smrg 
200181254a7Smrg     import std.utf : byChar, byWchar;
201181254a7Smrg     char[300] abc = 'a';
202*b1e83836Smrg     assert(arrayFor(tempCString(abc[].byChar).buffPtr) == abc);
203*b1e83836Smrg     assert(arrayFor(tempCString(abc[].byWchar).buffPtr) == abc);
204181254a7Smrg     assert(tempCString(abc[].byChar)[] == abc);
205181254a7Smrg }
206181254a7Smrg 
207*b1e83836Smrg // https://issues.dlang.org/show_bug.cgi?id=14980
208*b1e83836Smrg pure nothrow @nogc @safe unittest
209181254a7Smrg {
210181254a7Smrg     const(char[]) str = null;
211181254a7Smrg     auto res = tempCString(str);
212181254a7Smrg     const char* ptr = res;
213181254a7Smrg     assert(ptr is null);
214181254a7Smrg }
215181254a7Smrg 
version(Windows)216181254a7Smrg version (Windows)
217*b1e83836Smrg {
218*b1e83836Smrg     import core.sys.windows.winnt : WCHAR;
219*b1e83836Smrg     alias tempCStringW = tempCString!(WCHAR, const(char)[]);
220*b1e83836Smrg }
221*b1e83836Smrg 
222*b1e83836Smrg private struct TempCStringBuffer(To = char)
223*b1e83836Smrg {
224*b1e83836Smrg @trusted pure nothrow @nogc:
225*b1e83836Smrg 
226*b1e83836Smrg     @disable this();
227*b1e83836Smrg     @disable this(this);
228*b1e83836Smrg     alias ptr this; /// implicitly covert to raw pointer
229*b1e83836Smrg 
230*b1e83836Smrg     @property inout(To)* buffPtr() return inout
231*b1e83836Smrg     {
232*b1e83836Smrg         return _ptr == useStack ? _buff.ptr : _ptr;
233*b1e83836Smrg     }
234*b1e83836Smrg 
235*b1e83836Smrg     @property const(To)* ptr() const
236*b1e83836Smrg     {
237*b1e83836Smrg         return buffPtr;
238*b1e83836Smrg     }
239*b1e83836Smrg 
240*b1e83836Smrg     const(To)[] opIndex() const pure
241*b1e83836Smrg     {
242*b1e83836Smrg         return buffPtr[0 .. _length];
243*b1e83836Smrg     }
244*b1e83836Smrg 
245*b1e83836Smrg     ~this()
246*b1e83836Smrg     {
247*b1e83836Smrg         if (_ptr != useStack)
248*b1e83836Smrg         {
249*b1e83836Smrg             import core.memory : pureFree;
250*b1e83836Smrg             pureFree(_ptr);
251*b1e83836Smrg         }
252*b1e83836Smrg     }
253*b1e83836Smrg 
254*b1e83836Smrg private:
255*b1e83836Smrg     enum To* useStack = () @trusted { return cast(To*) size_t.max; }();
256*b1e83836Smrg 
257*b1e83836Smrg     To* _ptr;
258*b1e83836Smrg     size_t _length;        // length of the string
versionTempCStringBuffer259*b1e83836Smrg     version (StdUnittest)
260*b1e83836Smrg     // the 'small string optimization'
261*b1e83836Smrg     {
262*b1e83836Smrg         // smaller size to trigger reallocations. Padding is to account for
263*b1e83836Smrg         // unittest/non-unittest cross-compilation (to avoid corruption)
264*b1e83836Smrg         To[16 / To.sizeof] _buff;
265*b1e83836Smrg         To[(256 - 16) / To.sizeof] _unittest_pad;
266*b1e83836Smrg     }
267*b1e83836Smrg     else
268*b1e83836Smrg     {
269*b1e83836Smrg         To[256 / To.sizeof] _buff; // production size
270*b1e83836Smrg     }
271*b1e83836Smrg 
trustedVoidInitTempCStringBuffer272*b1e83836Smrg     static TempCStringBuffer trustedVoidInit() { TempCStringBuffer res = void; return res; }
273*b1e83836Smrg }
274*b1e83836Smrg 
trustedRealloc(To)275*b1e83836Smrg private To[] trustedRealloc(To)(return scope To[] buf)
276*b1e83836Smrg     @trusted @nogc pure nothrow
277*b1e83836Smrg {
278*b1e83836Smrg     pragma(inline, false);  // because it's rarely called
279*b1e83836Smrg     import std.internal.memory : enforceRealloc;
280*b1e83836Smrg 
281*b1e83836Smrg     const size_t newlen = buf.length * 3 / 2;
282*b1e83836Smrg     if (buf.length >= size_t.max / (2 * To.sizeof))
283*b1e83836Smrg     {
284*b1e83836Smrg         version (D_Exceptions)
285*b1e83836Smrg         {
286*b1e83836Smrg             import core.exception : onOutOfMemoryError;
287*b1e83836Smrg             onOutOfMemoryError();
288*b1e83836Smrg         }
289*b1e83836Smrg         else
290*b1e83836Smrg         {
291*b1e83836Smrg             assert(0, "Memory allocation failed");
292*b1e83836Smrg         }
293*b1e83836Smrg     }
294*b1e83836Smrg     auto ptr = cast(To*) enforceRealloc(buf.ptr, newlen * To.sizeof);
295*b1e83836Smrg     return ptr[0 .. newlen];
296*b1e83836Smrg 
297*b1e83836Smrg }
298*b1e83836Smrg 
trustedReallocStack(To)299*b1e83836Smrg private To[] trustedReallocStack(To)(scope To[] buf, size_t strLength)
300*b1e83836Smrg     @trusted @nogc pure nothrow
301*b1e83836Smrg {
302*b1e83836Smrg     pragma(inline, false);  // because it's rarely called
303*b1e83836Smrg 
304*b1e83836Smrg     import std.internal.memory : enforceMalloc;
305*b1e83836Smrg 
306*b1e83836Smrg     size_t newlen = buf.length * 3 / 2;
307*b1e83836Smrg     if (newlen <= strLength)
308*b1e83836Smrg         newlen = strLength + 1; // +1 for terminating 0
309*b1e83836Smrg     auto ptr = cast(To*) enforceMalloc(newlen * To.sizeof);
310*b1e83836Smrg     ptr[0 .. buf.length] = buf[];
311*b1e83836Smrg     return ptr[0 .. newlen];
312*b1e83836Smrg }
313