1 /** 2 * Common string functions including filename manipulation. 3 * 4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: Walter Bright, https://www.digitalmars.com 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d, common/_string.d) 8 * Documentation: https://dlang.org/phobos/dmd_common_string.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/string.d 10 */ 11 module dmd.common..string; 12 13 nothrow: 14 15 /** 16 Defines a temporary array of `Element`s using a fixed-length buffer as back store. If the length 17 of the buffer suffices, it is readily used. Otherwise, `malloc` is used to 18 allocate memory for the array and `free` is used for deallocation in the 19 destructor. 20 21 This type is meant to use exclusively as an automatic variable. It is not 22 default constructible or copyable. 23 */ 24 struct SmallBuffer(Element) 25 { 26 import core.stdc.stdlib : malloc, free; 27 28 private Element[] _extent; 29 private bool needsFree; 30 31 nothrow: 32 @nogc: 33 34 @disable this(); // no default ctor 35 @disable this(ref const SmallBuffer!Element); // noncopyable, nonassignable 36 37 /*********** 38 * Construct a SmallBuffer 39 * Params: 40 * len = number of elements in array 41 * buffer = slice to use as backing-store, if len will fit in it 42 */ 43 scope this(size_t len, return scope Element[] buffer) 44 { 45 if (len <= buffer.length) 46 { 47 _extent = buffer[0 .. len]; 48 } 49 else 50 { 51 assert(len < sizeof.max / Element.sizeof); 52 _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; 53 _extent.ptr || assert(0, "Out of memory."); 54 needsFree = true; 55 } 56 assert(this.length == len); 57 } 58 59 ~this() 60 { 61 if (needsFree) 62 free(_extent.ptr); 63 } 64 65 /****** 66 * Resize existing SmallBuffer. 67 * Params: 68 * len = number of elements after resize 69 */ 70 scope void create(size_t len) 71 { 72 if (len <= _extent.length) 73 { 74 _extent = _extent[0 .. len]; // reuse existing storage 75 } 76 else 77 { 78 __dtor(); 79 assert(len < sizeof.max / Element.sizeof); 80 _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; 81 _extent.ptr || assert(0, "Out of memory."); 82 needsFree = true; 83 } 84 assert(this.length == len); 85 } 86 87 // Force accesses to extent to be scoped. 88 scope inout extent() 89 { 90 return _extent; 91 } 92 93 alias extent this; 94 } 95 96 /// ditto 97 unittest 98 { 99 char[230] buf = void; 100 auto a = SmallBuffer!char(10, buf); 101 assert(a[] is buf[0 .. 10]); 102 auto b = SmallBuffer!char(1000, buf); 103 assert(b[] !is buf[]); 104 b.create(1000); 105 assert(b.length == 1000); 106 assert(b[] !is buf[]); 107 } 108 109 /** 110 Converts a zero-terminated C string to a D slice. Takes linear time and allocates no memory. 111 112 Params: 113 stringz = the C string to be converted 114 115 Returns: 116 a slice comprehending the string. The terminating 0 is not part of the slice. 117 */ 118 auto asDString(C)(C* stringz) pure @nogc nothrow 119 { 120 import core.stdc.string : strlen; 121 return stringz[0 .. strlen(stringz)]; 122 } 123 124 /// 125 unittest 126 { 127 const char* p = "123".ptr; 128 assert(p.asDString == "123"); 129 } 130 131 /** 132 (Windows only) Converts a narrow string to a wide string using `buffer` as strorage. Returns a slice managed by 133 `buffer` containing the converted string. The terminating zero is not part of the returned slice, 134 but is guaranteed to follow it. 135 */ 136 version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow 137 { 138 import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar; 139 // assume filenames encoded in system default Windows ANSI code page 140 enum CodePage = CP_ACP; 141 142 if (narrow is null) 143 return null; 144 145 const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length); 146 if (requiredLength < cast(int) buffer.length) 147 { 148 buffer[requiredLength] = 0; 149 return buffer[0 .. requiredLength]; 150 } 151 152 buffer.create(requiredLength + 1); 153 const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, requiredLength); 154 assert(length == requiredLength); 155 buffer[length] = 0; 156 return buffer[0 .. length]; 157 } 158 159 /************************************** 160 * Converts a path to one suitable to be passed to Win32 API 161 * functions that can deal with paths longer than 248 162 * characters then calls the supplied function on it. 163 * 164 * Params: 165 * path = The Path to call F on. 166 * 167 * Returns: 168 * The result of calling F on path. 169 * 170 * References: 171 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx 172 */ 173 version(Windows) auto extendedPathThen(alias F)(const(char)[] path) 174 { 175 import core.sys.windows.winbase; 176 import core.sys.windows.winnt; 177 178 if (!path.length) 179 return F((wchar[]).init); 180 181 wchar[1024] buf = void; 182 auto store = SmallBuffer!wchar(buf.length, buf); 183 auto wpath = toWStringz(path, store); 184 185 // GetFullPathNameW expects a sized buffer to store the result in. Since we don't 186 // know how large it has to be, we pass in null and get the needed buffer length 187 // as the return code. 188 const pathLength = GetFullPathNameW(&wpath[0], 189 0 /*length8*/, 190 null /*output buffer*/, 191 null /*filePartBuffer*/); 192 if (pathLength == 0) 193 { 194 return F((wchar[]).init); 195 } 196 197 // wpath is the UTF16 version of path, but to be able to use 198 // extended paths, we need to prefix with `\\?\` and the absolute 199 // path. 200 static immutable prefix = `\\?\`w; 201 202 // prefix only needed for long names and non-UNC names 203 const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\'); 204 const prefixLength = needsPrefix ? prefix.length : 0; 205 206 // +1 for the null terminator 207 const bufferLength = pathLength + prefixLength + 1; 208 209 wchar[1024] absBuf = void; 210 auto absPath = SmallBuffer!wchar(bufferLength, absBuf); 211 212 absPath[0 .. prefixLength] = prefix[0 .. prefixLength]; 213 214 const absPathRet = GetFullPathNameW(&wpath[0], 215 cast(uint)(absPath.length - prefixLength - 1), 216 &absPath[prefixLength], 217 null /*filePartBuffer*/); 218 219 if (absPathRet == 0 || absPathRet > absPath.length - prefixLength) 220 { 221 return F((wchar[]).init); 222 } 223 224 absPath[$ - 1] = '\0'; 225 // Strip null terminator from the slice 226 return F(absPath[0 .. $ - 1]); 227 }