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