1 /** 2 * Common string functions including filename manipulation. 3 * 4 * Copyright: Copyright (C) 1999-2022 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 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(T) 25 { 26 import core.stdc.stdlib : malloc, free; 27 28 private T[] _extent; 29 private bool needsFree; 30 31 nothrow: 32 33 @disable this(); // no default ctor 34 @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable 35 36 this(size_t len, T[] buffer) 37 { 38 if (len <= buffer.length) 39 { 40 _extent = buffer[0 .. len]; 41 } 42 else 43 { 44 _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; 45 _extent.ptr || assert(0, "Out of memory."); 46 needsFree = true; 47 } 48 assert(this.length == len); 49 } 50 51 ~this() 52 { 53 if (needsFree) 54 free(_extent.ptr); 55 } 56 57 void create(size_t len) 58 { 59 if (len <= _extent.length) 60 { 61 _extent = _extent[0 .. len]; 62 } 63 else 64 { 65 __dtor(); 66 _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; 67 _extent.ptr || assert(0, "Out of memory."); 68 needsFree = true; 69 } 70 assert(this.length == len); 71 } 72 73 // Force accesses to extent to be scoped. 74 scope inout extent() 75 { 76 return _extent; 77 } 78 79 alias extent this; 80 } 81 82 /// ditto 83 unittest 84 { 85 char[230] buf = void; 86 auto a = SmallBuffer!char(10, buf); 87 assert(a[] is buf[0 .. 10]); 88 auto b = SmallBuffer!char(1000, buf); 89 assert(b[] !is buf[]); 90 b.create(1000); 91 assert(b.length == 1000); 92 assert(b[] !is buf[]); 93 } 94 95 /** 96 Converts a zero-terminated C string to a D slice. Takes linear time and allocates no memory. 97 98 Params: 99 stringz = the C string to be converted 100 101 Returns: 102 a slice comprehending the string. The terminating 0 is not part of the slice. 103 */ 104 auto asDString(C)(C* stringz) pure @nogc nothrow 105 { 106 import core.stdc.string : strlen; 107 return stringz[0 .. strlen(stringz)]; 108 } 109 110 /// 111 unittest 112 { 113 const char* p = "123".ptr; 114 assert(p.asDString == "123"); 115 } 116 117 /** 118 (Windows only) Converts a narrow string to a wide string using `buffer` as strorage. Returns a slice managed by 119 `buffer` containing the converted string. The terminating zero is not part of the returned slice, 120 but is guaranteed to follow it. 121 */ 122 version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow 123 { 124 import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar; 125 // assume filenames encoded in system default Windows ANSI code page 126 enum CodePage = CP_ACP; 127 128 if (narrow is null) 129 return null; 130 131 const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length); 132 if (requiredLength < cast(int) buffer.length) 133 { 134 buffer[requiredLength] = 0; 135 return buffer[0 .. requiredLength]; 136 } 137 138 buffer.create(requiredLength + 1); 139 const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, requiredLength); 140 assert(length == requiredLength); 141 buffer[length] = 0; 142 return buffer[0 .. length]; 143 } 144 145 /************************************** 146 * Converts a path to one suitable to be passed to Win32 API 147 * functions that can deal with paths longer than 248 148 * characters then calls the supplied function on it. 149 * 150 * Params: 151 * path = The Path to call F on. 152 * 153 * Returns: 154 * The result of calling F on path. 155 * 156 * References: 157 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx 158 */ 159 version(Windows) auto extendedPathThen(alias F)(const(char)[] path) 160 { 161 import core.sys.windows.winbase; 162 import core.sys.windows.winnt; 163 164 if (!path.length) 165 return F((wchar[]).init); 166 167 wchar[1024] buf = void; 168 auto store = SmallBuffer!wchar(buf.length, buf); 169 auto wpath = toWStringz(path, store); 170 171 // GetFullPathNameW expects a sized buffer to store the result in. Since we don't 172 // know how large it has to be, we pass in null and get the needed buffer length 173 // as the return code. 174 const pathLength = GetFullPathNameW(&wpath[0], 175 0 /*length8*/, 176 null /*output buffer*/, 177 null /*filePartBuffer*/); 178 if (pathLength == 0) 179 { 180 return F((wchar[]).init); 181 } 182 183 // wpath is the UTF16 version of path, but to be able to use 184 // extended paths, we need to prefix with `\\?\` and the absolute 185 // path. 186 static immutable prefix = `\\?\`w; 187 188 // prefix only needed for long names and non-UNC names 189 const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\'); 190 const prefixLength = needsPrefix ? prefix.length : 0; 191 192 // +1 for the null terminator 193 const bufferLength = pathLength + prefixLength + 1; 194 195 wchar[1024] absBuf = void; 196 auto absPath = SmallBuffer!wchar(bufferLength, absBuf); 197 198 absPath[0 .. prefixLength] = prefix[0 .. prefixLength]; 199 200 const absPathRet = GetFullPathNameW(&wpath[0], 201 cast(uint)(absPath.length - prefixLength - 1), 202 &absPath[prefixLength], 203 null /*filePartBuffer*/); 204 205 if (absPathRet == 0 || absPathRet > absPath.length - prefixLength) 206 { 207 return F((wchar[]).init); 208 } 209 210 absPath[$ - 1] = '\0'; 211 // Strip null terminator from the slice 212 return F(absPath[0 .. $ - 1]); 213 } 214