312 lines · 8.5 KB · D
RawDownload
/**Helper functions for working with $(I C strings). This module is intended to provide fast, safe and garbage freeway 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, `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(scope const(char)[] name, scope const(char)[] value)        { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); }    }     version (Windows)    {        import core.sys.windows.winbase : SetEnvironmentVariableW;        import std.exception : enforce;         void setEnvironment(scope const(char)[] name, scope const(char)[] value)        { enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); }    }} import std.range;import std.traits; /**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*) andhas two properties: `ptr` to access $(I C string) as $(D const To*)and `buffPtr` to access it as `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 isvalid unless the variable goes out of scope. If returned object isn'tassigned to a variable it will be destroyed at the end of creatingprimary expression. Implementation_note:For small strings tempCString will use stack allocated buffer,for large strings (approximately 250 characters and more) it willallocate temporary one using C's `malloc`. Note:This function is intended to be used in function call expression (like`strlen(str.tempCString())`). Incorrect usage of this function maylead to memory corruption.See $(RED WARNING) in $(B Examples) section.*/ auto tempCString(To = char, From)(scope From str)if (isSomeChar!To && (isInputRange!From || isSomeString!From) &&    isSomeChar!(ElementEncodingType!From)){    alias CF = Unqual!(ElementEncodingType!From);     auto res = TempCStringBuffer!To.trustedVoidInit(); // expensive to fill _buff[]     // Note: res._ptr can't point to res._buff as structs are movable.     // https://issues.dlang.org/show_bug.cgi?id=14980    static if (isSomeString!From)    {        if (str is null)        {            res._length = 0;            res._ptr = null;            return res;        }    }     // Use slice assignment if available.    static if (To.sizeof == CF.sizeof && is(typeof(res._buff[0 .. str.length] = str[])))    {        if (str.length < res._buff.length)        {            res._buff[0 .. str.length] = str[];            res._buff[str.length] = 0;            res._ptr = res.useStack;        }        else        {            import std.internal.memory : enforceMalloc;            if (false)            {                // This code is removed by the compiler but causes `@safe`ty                // to be inferred correctly.                CF[0] x;                x[] = str[0 .. 0];            }            res._ptr = () @trusted {                auto p = cast(CF*) enforceMalloc((str.length + 1) * CF.sizeof);                p[0 .. str.length] = str[];                p[str.length] = 0;                return cast(To*) p;            }();        }        res._length = str.length;        return res;    }    else    {        static assert(!(isSomeString!From && CF.sizeof == To.sizeof), "Should be using slice assignment.");        To[] p = res._buff;        size_t i;         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        else            alias r = str;        To[] heapBuffer;        foreach (const c; byUTF!(Unqual!To)(r))        {            if (i + 1 == p.length)            {                if (heapBuffer is null)                    heapBuffer = trustedReallocStack(p, strLength);                else                    heapBuffer = trustedRealloc(heapBuffer);                p = heapBuffer;            }            p[i++] = c;        }        p[i] = 0;        res._length = i;        res._ptr = (heapBuffer is null ? res.useStack : &heapBuffer[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 pure nothrow @nogc unittest{    static inout(C)[] arrayFor(C)(inout(C)* cstr) pure nothrow @nogc @trusted    {        assert(cstr);        size_t length = 0;        while (cstr[length])            ++length;        return cstr[0 .. length];    }     assert(arrayFor("abc".tempCString()) == "abc");    assert(arrayFor("abc"d.tempCString().ptr) == "abc");    assert(arrayFor("abc".tempCString!wchar().buffPtr) == "abc"w);     import std.utf : byChar, byWchar;    char[300] abc = 'a';    assert(arrayFor(tempCString(abc[].byChar).buffPtr) == abc);    assert(arrayFor(tempCString(abc[].byWchar).buffPtr) == abc);    assert(tempCString(abc[].byChar)[] == abc);} // https://issues.dlang.org/show_bug.cgi?id=14980pure nothrow @nogc @safe unittest{    const(char[]) str = null;    auto res = tempCString(str);    const char* ptr = res;    assert(ptr is null);} version (Windows){    import core.sys.windows.winnt : WCHAR;    alias tempCStringW = tempCString!(WCHAR, const(char)[]);} private struct TempCStringBuffer(To = char){@trusted pure nothrow @nogc:     @disable this();    @disable this(this);    alias ptr this; /// implicitly covert to raw pointer     @property inout(To)* buffPtr() return inout    {        return _ptr == useStack ? _buff.ptr : _ptr;    }     @property const(To)* ptr() const    {        return buffPtr;    }     const(To)[] opIndex() const pure    {        return buffPtr[0 .. _length];    }     ~this()    {        if (_ptr != useStack)        {            import core.memory : pureFree;            pureFree(_ptr);        }    } private:    enum To* useStack = () @trusted { return cast(To*) size_t.max; }();     To* _ptr;    size_t _length;        // length of the string    version (StdUnittest)    // the 'small string optimization'    {        // 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 TempCStringBuffer trustedVoidInit() { TempCStringBuffer res = void; return res; }} private To[] trustedRealloc(To)(return scope To[] buf)    @trusted @nogc pure nothrow{    pragma(inline, false);  // because it's rarely called    import std.internal.memory : enforceRealloc;     const size_t newlen = buf.length * 3 / 2;    if (buf.length >= size_t.max / (2 * To.sizeof))    {        version (D_Exceptions)        {            import core.exception : onOutOfMemoryError;            onOutOfMemoryError();        }        else        {            assert(0, "Memory allocation failed");        }    }    auto ptr = cast(To*) enforceRealloc(buf.ptr, newlen * To.sizeof);    return ptr[0 .. newlen]; } private To[] trustedReallocStack(To)(scope To[] buf, size_t strLength)    @trusted @nogc pure nothrow{    pragma(inline, false);  // because it's rarely called     import std.internal.memory : enforceMalloc;     size_t newlen = buf.length * 3 / 2;    if (newlen <= strLength)        newlen = strLength + 1; // +1 for terminating 0    auto ptr = cast(To*) enforceMalloc(newlen * To.sizeof);    ptr[0 .. buf.length] = buf[];    return ptr[0 .. newlen];}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t