mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 14:40:30 +03:00
333 lines
8.8 KiB
D
333 lines
8.8 KiB
D
/**
|
|
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).
|
|
|
|
Examples:
|
|
---
|
|
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())); }
|
|
---
|
|
|
|
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;
|
|
|
|
|
|
import std.traits;
|
|
|
|
version(unittest)
|
|
@property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc
|
|
if(isSomeChar!C)
|
|
in { assert(cstr); }
|
|
body
|
|
{
|
|
size_t length = 0;
|
|
while(cstr[length])
|
|
++length;
|
|
return cstr[0 .. length];
|
|
}
|
|
|
|
/**
|
|
Creates temporary $(I C string) with copy of passed text.
|
|
|
|
Returned object 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 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)(in From[] str) nothrow @nogc
|
|
if(isSomeChar!To && isSomeChar!From)
|
|
{
|
|
import core.checkedint : addu;
|
|
import core.exception : onOutOfMemoryError;
|
|
|
|
enum useStack = cast(To*) -1;
|
|
|
|
static struct Res
|
|
{
|
|
nothrow @nogc:
|
|
|
|
@disable this();
|
|
@disable this(this);
|
|
alias ptr this;
|
|
|
|
@property inout(To)* buffPtr() inout @safe pure
|
|
{ return _ptr == useStack ? _buff.ptr : _ptr; }
|
|
|
|
@property const(To)* ptr() const @safe pure
|
|
{ return buffPtr; }
|
|
|
|
~this()
|
|
{ if(_ptr != useStack) rawFree(_ptr); }
|
|
|
|
private:
|
|
To* _ptr;
|
|
To[256] _buff;
|
|
}
|
|
|
|
// TODO: Don't stack allocate uninitialized array to
|
|
// not confuse unprecise GC.
|
|
|
|
Res res = void;
|
|
if(!str.ptr)
|
|
{
|
|
res._ptr = null;
|
|
return res;
|
|
}
|
|
|
|
// Note: res._ptr can't point to res._buff as structs are movable.
|
|
|
|
bool overflow = false;
|
|
const totalCount = addu(maxLength!To(str), 1, overflow);
|
|
if(overflow)
|
|
onOutOfMemoryError();
|
|
const needAllocate = totalCount > res._buff.length;
|
|
To[] arr = copyEncoded(str, needAllocate ?
|
|
allocate!To(totalCount)[0 .. $ - 1] : res._buff[0 .. totalCount - 1]);
|
|
*(arr.ptr + arr.length) = '\0';
|
|
res._ptr = needAllocate ? arr.ptr : useStack;
|
|
return res;
|
|
}
|
|
|
|
///
|
|
nothrow @nogc 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.
|
|
}
|
|
|
|
nothrow @nogc unittest
|
|
{
|
|
assert("abc".tempCString().asArray == "abc");
|
|
assert("abc"d.tempCString().ptr.asArray == "abc");
|
|
assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w);
|
|
}
|
|
|
|
// Test for Issue 13367: ensure there is no memory corruption
|
|
nothrow @nogc unittest
|
|
{
|
|
@property str(C)() { C[300] arr = 'a'; return arr; }
|
|
assert(str!char.tempCString!wchar().asArray == str!wchar);
|
|
assert(str!char.tempCString!dchar().asArray == str!dchar);
|
|
}
|
|
|
|
version(Windows)
|
|
alias tempCStringW = tempCString!(wchar, char);
|
|
|
|
|
|
private:
|
|
|
|
|
|
// Helper UTF functions.
|
|
// ----------------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
Returns maximum possible length of string conversion
|
|
to another Unicode Transformation Format result.
|
|
*/
|
|
size_t maxLength(To, From)(in size_t length) pure nothrow @nogc
|
|
if(isSomeChar!To && isSomeChar!From)
|
|
{
|
|
static if (To.sizeof >= From.sizeof)
|
|
enum k = 1; // worst case: every code unit represents a character
|
|
else static if (To.sizeof == 1 && From.sizeof == 2)
|
|
enum k = 3; // worst case: every wchar in top of BMP
|
|
else static if (To.sizeof == 1 && From.sizeof == 4)
|
|
enum k = 4; // worst case: every dchar not in BMP
|
|
else static if (To.sizeof == 2 && From.sizeof == 4)
|
|
enum k = 2; // worst case: every dchar not in BMP
|
|
else
|
|
static assert(0);
|
|
return length * k;
|
|
}
|
|
|
|
/// ditto
|
|
size_t maxLength(To, From)(in From[] str) pure nothrow @nogc
|
|
{ return maxLength!(To, From)(str.length); }
|
|
|
|
pure nothrow @nogc unittest
|
|
{
|
|
assert(maxLength!char("abc") == 3);
|
|
assert(maxLength!dchar("abc") == 3);
|
|
assert(maxLength!char("abc"w) == 9);
|
|
assert(maxLength!char("abc"d) == 12);
|
|
assert(maxLength!wchar("abc"d) == 6);
|
|
}
|
|
|
|
|
|
/**
|
|
Copies text from $(D source) to $(D buff) performing conversion
|
|
to different Unicode Transformation Format if needed.
|
|
|
|
$(D buff) must be large enough to hold the result.
|
|
|
|
Returns:
|
|
Slice of the provided buffer $(D buff) with the copy of $(D source).
|
|
*/
|
|
To[] copyEncoded(To, From)(in From[] source, To[] buff) @trusted nothrow @nogc
|
|
if(isSomeChar!To && isSomeChar!From)
|
|
{
|
|
static if(is(Unqual!To == Unqual!From))
|
|
{
|
|
return buff[0 .. source.length] = source[];
|
|
}
|
|
else
|
|
{
|
|
import std.utf : byChar, byWchar, byDchar;
|
|
alias GenericTuple(Args...) = Args;
|
|
alias byFunc = GenericTuple!(byChar, byWchar, null, byDchar)[To.sizeof - 1];
|
|
|
|
To* ptr = buff.ptr;
|
|
const To* last = ptr + buff.length;
|
|
foreach(const c; byFunc(source))
|
|
{
|
|
assert(ptr != last);
|
|
*ptr++ = c;
|
|
}
|
|
return buff[0 .. ptr - buff.ptr];
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @nogc unittest
|
|
{
|
|
const str = "abc-ЭЮЯ";
|
|
wchar[100] wsbuff;
|
|
assert(copyEncoded(str, wsbuff) == "abc-ЭЮЯ"w);
|
|
}
|
|
|
|
pure nothrow @nogc unittest
|
|
{
|
|
wchar[100] wsbuff;
|
|
assert(copyEncoded("abc-ЭЮЯ"w, wsbuff) == "abc-ЭЮЯ"w);
|
|
}
|
|
|
|
pure unittest
|
|
{
|
|
import std.range;
|
|
import std.utf : toUTF16, toUTF32;
|
|
|
|
const str = "abc-ЭЮЯ";
|
|
char[100] sbuff;
|
|
|
|
{
|
|
wchar[100] wsbuff;
|
|
const strW = toUTF16(str);
|
|
assert(copyEncoded(str, wsbuff[0 .. strW.length]) == strW);
|
|
assert(copyEncoded(strW, sbuff[0 .. str.length]) == str);
|
|
}
|
|
{
|
|
dchar[100] dsbuff;
|
|
const strD = toUTF32(str);
|
|
assert(copyEncoded(str, dsbuff[0 .. walkLength(str)]) == strD);
|
|
assert(copyEncoded(strD, sbuff[0 .. str.length]) == str);
|
|
}
|
|
}
|
|
|
|
|
|
// Helper functions for memory allocation & freeing.
|
|
// ----------------------------------------------------------------------------------------------------
|
|
|
|
// WARNING: Alignment is implementation defined in C so the value '4'
|
|
// relies on undocumented but common behaviour.
|
|
// FIXME: This value should be checked and adjusted on every C runtime.
|
|
enum mallocAlignment = 4;
|
|
|
|
// NOTE: `allocate`/`rawFree` simply wraps C allocation functions for now
|
|
// but it may be changed in future so one shouldn't rely on that.
|
|
|
|
T[] allocate(T)(in size_t count) nothrow @nogc
|
|
if(T.alignof <= mallocAlignment)
|
|
in { assert(count); }
|
|
body
|
|
{
|
|
import core.exception : onOutOfMemoryError;
|
|
import core.checkedint: mulu;
|
|
|
|
bool overflow = false;
|
|
const buffBytes = mulu(T.sizeof, count, overflow);
|
|
if(overflow)
|
|
onOutOfMemoryError();
|
|
|
|
auto ptr = cast(T*) tryRawAllocate(buffBytes);
|
|
if(!ptr)
|
|
onOutOfMemoryError();
|
|
|
|
return ptr[0 .. count];
|
|
}
|
|
|
|
void* tryRawAllocate(in size_t count) nothrow @nogc
|
|
in { assert(count); }
|
|
body
|
|
{
|
|
import core.stdc.stdlib: malloc;
|
|
// Workaround snn @@@BUG11646@@@
|
|
version(DigitalMars) version(Win32)
|
|
if(count > 0xD5550000) return null;
|
|
|
|
// FIXME: `malloc` must be checked on every C runtime for
|
|
// possible bugs and workarounded if necessary.
|
|
|
|
return malloc(count);
|
|
}
|
|
|
|
void rawFree(void* ptr) nothrow @nogc
|
|
{
|
|
import core.stdc.stdlib: free;
|
|
free(ptr);
|
|
}
|