Skip to content

Commit 8b41d4c

Browse files
authored
Spanize interop in System.Net.NetworkInformation (#35098)
Faster and safer
1 parent e1fa787 commit 8b41d4c

File tree

3 files changed

+43
-35
lines changed

3 files changed

+43
-35
lines changed

src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -424,19 +424,17 @@ internal struct MibTcp6TableOwnerPid
424424
}
425425

426426
[StructLayout(LayoutKind.Sequential)]
427-
internal struct MibTcp6RowOwnerPid
427+
internal unsafe struct MibTcp6RowOwnerPid
428428
{
429-
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
430-
internal byte[] localAddr;
429+
internal fixed byte localAddr[16];
431430
internal uint localScopeId;
432431
internal byte localPort1;
433432
internal byte localPort2;
434433
// Ports are only 16 bit values (in network WORD order, 3,4,1,2).
435434
// There are reports where the high order bytes have garbage in them.
436435
internal byte ignoreLocalPort3;
437436
internal byte ignoreLocalPort4;
438-
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
439-
internal byte[] remoteAddr;
437+
internal fixed byte remoteAddr[16];
440438
internal uint remoteScopeId;
441439
internal byte remotePort1;
442440
internal byte remotePort2;
@@ -446,6 +444,9 @@ internal struct MibTcp6RowOwnerPid
446444
internal byte ignoreRemotePort4;
447445
internal TcpState state;
448446
internal uint owningPid;
447+
448+
internal ReadOnlySpan<byte> localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16);
449+
internal ReadOnlySpan<byte> remoteAddrAsSpan => MemoryMarshal.CreateSpan(ref remoteAddr[0], 16);
449450
}
450451

451452
internal enum TcpTableClass
@@ -493,10 +494,9 @@ internal struct MibUdp6TableOwnerPid
493494
}
494495

495496
[StructLayout(LayoutKind.Sequential)]
496-
internal struct MibUdp6RowOwnerPid
497+
internal unsafe struct MibUdp6RowOwnerPid
497498
{
498-
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
499-
internal byte[] localAddr;
499+
internal fixed byte localAddr[16];
500500
internal uint localScopeId;
501501
internal byte localPort1;
502502
internal byte localPort2;
@@ -505,6 +505,8 @@ internal struct MibUdp6RowOwnerPid
505505
internal byte ignoreLocalPort3;
506506
internal byte ignoreLocalPort4;
507507
internal uint owningPid;
508+
509+
internal ReadOnlySpan<byte> localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16);
508510
}
509511

510512
internal delegate void StableUnicastIpAddressTableDelegate(IntPtr context, IntPtr table);

src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public override IPEndPoint[] GetActiveTcpListeners()
106106

107107
///
108108
/// Gets the active TCP connections. Uses the native GetTcpTable API.
109-
private List<SystemTcpConnectionInformation> GetAllTcpConnections()
109+
private unsafe List<SystemTcpConnectionInformation> GetAllTcpConnections()
110110
{
111111
uint size = 0;
112112
uint result = 0;
@@ -128,21 +128,20 @@ private List<SystemTcpConnectionInformation> GetAllTcpConnections()
128128

129129
if (result == Interop.IpHlpApi.ERROR_SUCCESS)
130130
{
131+
var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
132+
131133
// The table info just gives us the number of rows.
132-
Interop.IpHlpApi.MibTcpTable tcpTableInfo = Marshal.PtrToStructure<Interop.IpHlpApi.MibTcpTable>(buffer);
134+
ref readonly Interop.IpHlpApi.MibTcpTable tcpTableInfo = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcpTable>(span);
133135

134136
if (tcpTableInfo.numberOfEntries > 0)
135137
{
136138
// Skip over the tableinfo to get the inline rows.
137-
IntPtr newPtr = (IntPtr)((long)buffer + Marshal.SizeOf(tcpTableInfo.numberOfEntries));
139+
span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpTable));
138140

139141
for (int i = 0; i < tcpTableInfo.numberOfEntries; i++)
140142
{
141-
Interop.IpHlpApi.MibTcpRow tcpRow = Marshal.PtrToStructure<Interop.IpHlpApi.MibTcpRow>(newPtr);
142-
tcpConnections.Add(new SystemTcpConnectionInformation(tcpRow));
143-
144-
// Increment the pointer to the next row.
145-
newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(tcpRow));
143+
tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcpRow>(span)));
144+
span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpRow));
146145
}
147146
}
148147
}
@@ -179,21 +178,22 @@ private List<SystemTcpConnectionInformation> GetAllTcpConnections()
179178
Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0);
180179
if (result == Interop.IpHlpApi.ERROR_SUCCESS)
181180
{
181+
var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
182+
182183
// The table info just gives us the number of rows.
183-
Interop.IpHlpApi.MibTcp6TableOwnerPid tcpTable6OwnerPid = Marshal.PtrToStructure<Interop.IpHlpApi.MibTcp6TableOwnerPid>(buffer);
184+
ref readonly Interop.IpHlpApi.MibTcp6TableOwnerPid tcpTable6OwnerPid = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcp6TableOwnerPid>(span);
184185

185186
if (tcpTable6OwnerPid.numberOfEntries > 0)
186187
{
187188
// Skip over the tableinfo to get the inline rows.
188-
IntPtr newPtr = (IntPtr)((long)buffer + Marshal.SizeOf(tcpTable6OwnerPid.numberOfEntries));
189+
span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6TableOwnerPid));
189190

190191
for (int i = 0; i < tcpTable6OwnerPid.numberOfEntries; i++)
191192
{
192-
Interop.IpHlpApi.MibTcp6RowOwnerPid tcp6RowOwnerPid = Marshal.PtrToStructure<Interop.IpHlpApi.MibTcp6RowOwnerPid>(newPtr);
193-
tcpConnections.Add(new SystemTcpConnectionInformation(tcp6RowOwnerPid));
193+
tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef<Interop.IpHlpApi.MibTcp6RowOwnerPid>(span)));
194194

195195
// We increment the pointer to the next row.
196-
newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(tcp6RowOwnerPid));
196+
span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid));
197197
}
198198
}
199199
}
@@ -215,7 +215,7 @@ private List<SystemTcpConnectionInformation> GetAllTcpConnections()
215215
}
216216

217217
/// Gets the active UDP listeners. Uses the native GetUdpTable API.
218-
public override IPEndPoint[] GetActiveUdpListeners()
218+
public unsafe override IPEndPoint[] GetActiveUdpListeners()
219219
{
220220
uint size = 0;
221221
uint result = 0;
@@ -237,22 +237,25 @@ public override IPEndPoint[] GetActiveUdpListeners()
237237

238238
if (result == Interop.IpHlpApi.ERROR_SUCCESS)
239239
{
240+
var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
241+
240242
// The table info just gives us the number of rows.
241-
Interop.IpHlpApi.MibUdpTable udpTableInfo = Marshal.PtrToStructure<Interop.IpHlpApi.MibUdpTable>(buffer);
243+
ref readonly Interop.IpHlpApi.MibUdpTable udpTableInfo = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibUdpTable>(span);
242244

243245
if (udpTableInfo.numberOfEntries > 0)
244246
{
245247
// Skip over the tableinfo to get the inline rows.
246-
IntPtr newPtr = (IntPtr)((long)buffer + Marshal.SizeOf(udpTableInfo.numberOfEntries));
248+
span = span.Slice(sizeof(Interop.IpHlpApi.MibUdpTable));
249+
247250
for (int i = 0; i < udpTableInfo.numberOfEntries; i++)
248251
{
249-
Interop.IpHlpApi.MibUdpRow udpRow = Marshal.PtrToStructure<Interop.IpHlpApi.MibUdpRow>(newPtr);
252+
ref readonly Interop.IpHlpApi.MibUdpRow udpRow = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibUdpRow>(span);
250253
int localPort = udpRow.localPort1 << 8 | udpRow.localPort2;
251254

252255
udpListeners.Add(new IPEndPoint(udpRow.localAddr, (int)localPort));
253256

254257
// We increment the pointer to the next row.
255-
newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(udpRow));
258+
span = span.Slice(sizeof(Interop.IpHlpApi.MibUdpRow));
256259
}
257260
}
258261
}
@@ -289,23 +292,26 @@ public override IPEndPoint[] GetActiveUdpListeners()
289292

290293
if (result == Interop.IpHlpApi.ERROR_SUCCESS)
291294
{
295+
var span = new ReadOnlySpan<byte>((byte*)buffer, (int)size);
296+
292297
// The table info just gives us the number of rows.
293-
Interop.IpHlpApi.MibUdp6TableOwnerPid udp6TableOwnerPid = Marshal.PtrToStructure<Interop.IpHlpApi.MibUdp6TableOwnerPid>(buffer);
298+
ref readonly Interop.IpHlpApi.MibUdp6TableOwnerPid udp6TableOwnerPid = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibUdp6TableOwnerPid>(span);
294299

295300
if (udp6TableOwnerPid.numberOfEntries > 0)
296301
{
297302
// Skip over the tableinfo to get the inline rows.
298-
IntPtr newPtr = (IntPtr)((long)buffer + Marshal.SizeOf(udp6TableOwnerPid.numberOfEntries));
303+
span = span.Slice(sizeof(Interop.IpHlpApi.MibUdp6TableOwnerPid));
304+
299305
for (int i = 0; i < udp6TableOwnerPid.numberOfEntries; i++)
300306
{
301-
Interop.IpHlpApi.MibUdp6RowOwnerPid udp6RowOwnerPid = Marshal.PtrToStructure<Interop.IpHlpApi.MibUdp6RowOwnerPid>(newPtr);
307+
ref readonly Interop.IpHlpApi.MibUdp6RowOwnerPid udp6RowOwnerPid = ref MemoryMarshal.AsRef<Interop.IpHlpApi.MibUdp6RowOwnerPid>(span);
302308
int localPort = udp6RowOwnerPid.localPort1 << 8 | udp6RowOwnerPid.localPort2;
303309

304-
udpListeners.Add(new IPEndPoint(new IPAddress(udp6RowOwnerPid.localAddr,
310+
udpListeners.Add(new IPEndPoint(new IPAddress(udp6RowOwnerPid.localAddrAsSpan,
305311
udp6RowOwnerPid.localScopeId), localPort));
306312

307313
// We increment the pointer to the next row.
308-
newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(udp6RowOwnerPid));
314+
span = span.Slice(sizeof(Interop.IpHlpApi.MibUdp6RowOwnerPid));
309315
}
310316
}
311317
}

src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ internal class SystemTcpConnectionInformation : TcpConnectionInformation
1111
private readonly IPEndPoint _remoteEndPoint;
1212
private readonly TcpState _state;
1313

14-
internal SystemTcpConnectionInformation(Interop.IpHlpApi.MibTcpRow row)
14+
internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcpRow row)
1515
{
1616
_state = row.state;
1717

@@ -25,7 +25,7 @@ internal SystemTcpConnectionInformation(Interop.IpHlpApi.MibTcpRow row)
2525
}
2626

2727
// IPV6 version of the Tcp row.
28-
internal SystemTcpConnectionInformation(Interop.IpHlpApi.MibTcp6RowOwnerPid row)
28+
internal unsafe SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcp6RowOwnerPid row)
2929
{
3030
_state = row.state;
3131

@@ -34,8 +34,8 @@ internal SystemTcpConnectionInformation(Interop.IpHlpApi.MibTcp6RowOwnerPid row)
3434
int localPort = row.localPort1 << 8 | row.localPort2;
3535
int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2);
3636

37-
_localEndPoint = new IPEndPoint(new IPAddress(row.localAddr, row.localScopeId), (int)localPort);
38-
_remoteEndPoint = new IPEndPoint(new IPAddress(row.remoteAddr, row.remoteScopeId), (int)remotePort);
37+
_localEndPoint = new IPEndPoint(new IPAddress(row.localAddrAsSpan, row.localScopeId), (int)localPort);
38+
_remoteEndPoint = new IPEndPoint(new IPAddress(row.remoteAddrAsSpan, row.remoteScopeId), (int)remotePort);
3939
}
4040

4141
public override TcpState State { get { return _state; } }

0 commit comments

Comments
 (0)