Skip to content

Commit ddd210e

Browse files
committed
Simplify by avoiding unmanaged memory allocation
1 parent fa6799b commit ddd210e

File tree

1 file changed

+29
-44
lines changed

1 file changed

+29
-44
lines changed

src/core/IronPython.Modules/fcntl.cs

Lines changed: 29 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private static extern int _ioctl_arm64(int fd, ulong request,
153153

154154

155155
// request will be int, uint or BigInteger, and in Python is limited to values that can fit in 32 bits (unchecked)
156-
// long should capture all allowed values
156+
// long should capture all allowed request values
157157
// return value is int, bytes, or LightException
158158
[LightThrowing]
159159
public static object ioctl(int fd, long request, [NotNone] IBufferProtocol arg, bool mutate_flag = true) {
@@ -169,7 +169,7 @@ public static object ioctl(int fd, long request, [NotNone] IBufferProtocol arg,
169169
buf = arg.GetBufferNoThrow(BufferFlags.Writable);
170170
}
171171
if (buf is not null) {
172-
bufSize = buf.AsReadOnlySpan().Length;
172+
bufSize = buf.AsSpan().Length; // check early if buf is indeed writable
173173
} else {
174174
buf = arg.GetBuffer(BufferFlags.Simple);
175175
bufSize = buf.AsReadOnlySpan().Length;
@@ -180,32 +180,25 @@ public static object ioctl(int fd, long request, [NotNone] IBufferProtocol arg,
180180
mutate_flag = false; // return a buffer, not integer
181181
}
182182
bool in_place = bufSize > defaultBufSize; // only large buffers are mutated in place
183-
Debug.Assert(!in_place || mutate_flag); // in_place implies mutate_flag
184183

185184
#if !NETCOREAPP
186185
throw new PlatformNotSupportedException("ioctl is not supported on Mono");
187186
#else
188187
try {
188+
Debug.Assert(!in_place || mutate_flag); // in_place implies mutate_flag
189+
190+
Span<byte> workSpan;
191+
if (in_place) {
192+
workSpan = buf.AsSpan();
193+
} else {
194+
workSpan = new byte[defaultBufSize + 1]; // +1 for extra NUL byte
195+
Debug.Assert(bufSize <= defaultBufSize);
196+
buf.AsReadOnlySpan().CopyTo(workSpan);
197+
}
198+
int result;
199+
Errno errno;
189200
unsafe {
190-
MemoryHandle hmem = default;
191-
void* ptr = null;
192-
if (in_place) {
193-
hmem = buf.Pin();
194-
} else {
195-
ptr = NativeMemory.AllocZeroed(defaultBufSize + 1); // +1 for extra nul byte
196-
}
197-
try {
198-
if (in_place) {
199-
ptr = hmem.Pointer;
200-
} else {
201-
Debug.Assert(bufSize <= defaultBufSize);
202-
var dest = new Span<byte>((byte*)ptr, bufSize);
203-
buf.AsReadOnlySpan().CopyTo(dest);
204-
}
205-
Debug.Assert(ptr != null);
206-
207-
int result;
208-
Errno errno;
201+
fixed (byte* ptr = workSpan) {
209202
do {
210203
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) {
211204
// workaround for Arm64 vararg calling convention (but not for ARM64EC on Windows)
@@ -214,30 +207,22 @@ public static object ioctl(int fd, long request, [NotNone] IBufferProtocol arg,
214207
result = _ioctl(fd, cmd, ptr);
215208
}
216209
} while (UnixMarshal.ShouldRetrySyscall(result, out errno));
210+
}
211+
}
217212

218-
if (result == -1) {
219-
return LightExceptions.Throw(PythonNT.GetOsError(NativeConvert.FromErrno(errno)));
220-
}
221-
if (mutate_flag) {
222-
if (!in_place) {
223-
var src = new Span<byte>((byte*)ptr, bufSize);
224-
src.CopyTo(buf.AsSpan());
225-
}
226-
return ScriptingRuntimeHelpers.Int32ToObject(result);
227-
} else {
228-
Debug.Assert(!in_place);
229-
byte[] response = new byte[bufSize];
230-
var src = new Span<byte>((byte*)ptr, bufSize);
231-
src.CopyTo(response);
232-
return Bytes.Make(response);
233-
}
234-
} finally {
235-
if (in_place) {
236-
hmem.Dispose();
237-
} else {
238-
NativeMemory.Free(ptr);
239-
}
213+
if (result == -1) {
214+
return LightExceptions.Throw(PythonNT.GetOsError(NativeConvert.FromErrno(errno)));
215+
}
216+
if (mutate_flag) {
217+
if (!in_place) {
218+
workSpan.Slice(0, bufSize).CopyTo(buf.AsSpan());
240219
}
220+
return ScriptingRuntimeHelpers.Int32ToObject(result);
221+
} else {
222+
Debug.Assert(!in_place);
223+
byte[] response = new byte[bufSize];
224+
workSpan.Slice(0, bufSize).CopyTo(response);
225+
return Bytes.Make(response);
241226
}
242227
} finally {
243228
buf.Dispose();

0 commit comments

Comments
 (0)