blob: e5bc7f744bc4f6c1dada02d8b310f01d240361eb [file] [log] [blame]
/**
Helper functions for working with $(I C strings).
This module is intended to provide fast, safe and garbage free
way to work with $(I C strings).
Copyright: Denis Shelomovskij 2013-2014
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Denis Shelomovskij
Macros:
COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, $(D core.$1.$2))
*/
module std.internal.cstring;
///
@safe unittest
{
version (Posix)
{
import core.stdc.stdlib : free;
import core.sys.posix.stdlib : setenv;
import std.exception : enforce;
void setEnvironment(in char[] name, in char[] value)
{ enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); }
}
version (Windows)
{
import core.sys.windows.windows : SetEnvironmentVariableW;
import std.exception : enforce;
void setEnvironment(in char[] name, in char[] value)
{ enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); }
}
}
import std.range;
import std.traits;
version (unittest)
@property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc @trusted
if (isSomeChar!C)
in { assert(cstr); }
body
{
size_t length = 0;
while (cstr[length])
++length;
return cstr[0 .. length];
}
/**
Creates temporary 0-terminated $(I C string) with copy of passed text.
Params:
To = character type of returned C string
str = string or input range to be converted
Returns:
The value returned is implicitly convertible to $(D const To*) and
has two properties: $(D ptr) to access $(I C string) as $(D const To*)
and $(D buffPtr) to access it as $(D To*).
The value returned can be indexed by [] to access it as an array.
The temporary $(I C string) is valid unless returned object is destroyed.
Thus if returned object is assigned to a variable the temporary is
valid unless the variable goes out of scope. If returned object isn't
assigned to a variable it will be destroyed at the end of creating
primary expression.
Implementation_note:
For small strings tempCString will use stack allocated buffer,
for large strings (approximately 250 characters and more) it will
allocate temporary one using C's $(D malloc).
Note:
This function is intended to be used in function call expression (like
$(D strlen(str.tempCString()))). Incorrect usage of this function may
lead to memory corruption.
See $(RED WARNING) in $(B Examples) section.
*/
auto tempCString(To = char, From)(From str)
if (isSomeChar!To && (isInputRange!From || isSomeString!From) &&
isSomeChar!(ElementEncodingType!From))
{
alias CF = Unqual!(ElementEncodingType!From);
enum To* useStack = () @trusted { return cast(To*) size_t.max; }();
static struct Res
{
@trusted:
nothrow @nogc:
@disable this();
@disable this(this);
alias ptr this;
@property inout(To)* buffPtr() inout pure
{
return _ptr == useStack ? _buff.ptr : _ptr;
}
@property const(To)* ptr() const pure
{
return buffPtr;
}
const(To)[] opIndex() const pure
{
return buffPtr[0 .. _length];
}
~this()
{
if (_ptr != useStack)
{
import core.stdc.stdlib : free;
free(_ptr);
}
}
private:
To* _ptr;
size_t _length; // length of the string
// the 'small string optimization'
version (unittest)
{
// smaller size to trigger reallocations. Padding is to account for
// unittest/non-unittest cross-compilation (to avoid corruption)
To[16 / To.sizeof] _buff;
To[(256 - 16) / To.sizeof] _unittest_pad;
}
else
{
To[256 / To.sizeof] _buff; // production size
}
static Res trustedVoidInit() { Res res = void; return res; }
}
Res res = Res.trustedVoidInit(); // expensive to fill _buff[]
// Note: res._ptr can't point to res._buff as structs are movable.
To[] p;
bool p_is_onstack = true;
size_t i;
static To[] trustedRealloc(To[] buf, size_t i, To[] res, size_t strLength, bool res_is_onstack)
@trusted @nogc nothrow
{
pragma(inline, false); // because it's rarely called
import core.exception : onOutOfMemoryError;
import core.stdc.stdlib : malloc, realloc;
import core.stdc.string : memcpy;
if (res_is_onstack)
{
size_t newlen = res.length * 3 / 2;
if (newlen <= strLength)
newlen = strLength + 1; // +1 for terminating 0
auto ptr = cast(To*) malloc(newlen * To.sizeof);
if (!ptr)
onOutOfMemoryError();
memcpy(ptr, res.ptr, i * To.sizeof);
return ptr[0 .. newlen];
}
else
{
if (buf.length >= size_t.max / (2 * To.sizeof))
onOutOfMemoryError();
const newlen = buf.length * 3 / 2;
auto ptr = cast(To*) realloc(buf.ptr, newlen * To.sizeof);
if (!ptr)
onOutOfMemoryError();
return ptr[0 .. newlen];
}
}
size_t strLength;
static if (hasLength!From)
{
strLength = str.length;
}
import std.utf : byUTF;
static if (isSomeString!From)
{
auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF
if (r is null) // Bugzilla 14980
{
res._ptr = null;
return res;
}
}
else
alias r = str;
To[] q = res._buff;
foreach (const c; byUTF!(Unqual!To)(r))
{
if (i + 1 == q.length)
{
p = trustedRealloc(p, i, res._buff, strLength, p_is_onstack);
p_is_onstack = false;
q = p;
}
q[i++] = c;
}
q[i] = 0;
res._length = i;
res._ptr = p_is_onstack ? useStack : &p[0];
return res;
}
///
nothrow @nogc @system unittest
{
import core.stdc.string;
string str = "abc";
// Intended usage
assert(strlen(str.tempCString()) == 3);
// Correct usage
auto tmp = str.tempCString();
assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
// $(RED WARNING): $(RED Incorrect usage)
auto pInvalid1 = str.tempCString().ptr;
const char* pInvalid2 = str.tempCString();
// Both pointers refer to invalid memory here as
// returned values aren't assigned to a variable and
// both primary expressions are ended.
}
@safe nothrow @nogc unittest
{
assert("abc".tempCString().asArray == "abc");
assert("abc"d.tempCString().ptr.asArray == "abc");
assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w);
import std.utf : byChar, byWchar;
char[300] abc = 'a';
assert(tempCString(abc[].byChar).buffPtr.asArray == abc);
assert(tempCString(abc[].byWchar).buffPtr.asArray == abc);
assert(tempCString(abc[].byChar)[] == abc);
}
// Bugzilla 14980
nothrow @nogc @safe unittest
{
const(char[]) str = null;
auto res = tempCString(str);
const char* ptr = res;
assert(ptr is null);
}
version (Windows)
alias tempCStringW = tempCString!(wchar, const(char)[]);