The OpenD Programming Language

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 }