1 /** 2 D string to C string. 3 4 Copyright: Copyright Guillaume Piolat 2022 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module gamut.internals.cstring; 8 9 import core.stdc.stdlib: malloc, free; 10 11 // 12 // Low-cost C string conversions 13 // 14 alias CString = CStringImpl!char; 15 alias CString16 = CStringImpl!wchar; 16 17 /// Zero-terminated C string, to replace toStringz and toUTF16z 18 struct CStringImpl(CharType) if (is(CharType: char) || is(CharType: wchar)) 19 { 20 public: 21 nothrow: 22 @nogc: 23 24 const(CharType)* storage = null; 25 alias storage this; 26 27 28 this(const(CharType)[] s) 29 { 30 // Always copy. We can't assume anything about the input. 31 size_t len = s.length; 32 CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof); 33 buffer[0..len] = s[0..len]; 34 buffer[len] = '\0'; 35 storage = buffer; 36 wasAllocated = true; 37 } 38 39 // The constructor taking immutable can safely assume that such memory 40 // has been allocated by the GC or malloc, or an allocator that align 41 // pointer on at least 4 bytes. 42 this(immutable(CharType)[] s) 43 { 44 // Same optimizations that for toStringz 45 if (s.length == 0) 46 { 47 enum emptyString = cast(CharType[])""; 48 storage = emptyString.ptr; 49 return; 50 } 51 52 /* Peek past end of s[], if it's 0, no conversion necessary. 53 * Note that the compiler will put a 0 past the end of static 54 * strings, and the storage allocator will put a 0 past the end 55 * of newly allocated char[]'s. 56 */ 57 const(CharType)* p = s.ptr + s.length; 58 // Is p dereferenceable? A simple test: if the p points to an 59 // address multiple of 4, then conservatively assume the pointer 60 // might be pointing to another block of memory, which might be 61 // unreadable. Otherwise, it's definitely pointing to valid 62 // memory. 63 if ((cast(size_t) p & 3) && *p == 0) 64 { 65 storage = s.ptr; 66 return; 67 } 68 69 size_t len = s.length; 70 CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof); 71 buffer[0..len] = s[0..len]; 72 buffer[len] = '\0'; 73 storage = buffer; 74 wasAllocated = true; 75 } 76 77 ~this() 78 { 79 if (wasAllocated) 80 free(cast(void*)storage); 81 } 82 83 @disable this(this); 84 85 private: 86 bool wasAllocated = false; 87 } 88 89 /// Semantic function to check that a D string implicitely conveys a 90 /// termination byte after the slice. 91 /// (typically those comes from string literals or `stringDup`/`stringIDup`) 92 const(char)* assumeZeroTerminated(const(char)[] input) pure nothrow @nogc @trusted 93 { 94 assert (input.ptr !is null); 95 96 // Check that the null character is there 97 assert(input.ptr[input.length] == '\0'); 98 return input.ptr; 99 }