Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not overallocate SocketAddress.Buffer #78860

Merged
merged 7 commits into from Dec 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/libraries/Common/src/System/Net/SocketAddress.cs
Expand Up @@ -92,7 +92,13 @@ public SocketAddress(AddressFamily family, int size)
}

InternalSize = size;
Buffer = new byte[(size / IntPtr.Size + 2) * IntPtr.Size];
#if !SYSTEM_NET_PRIMITIVES_DLL && WINDOWS
// WSARecvFrom needs a pinned pointer to the 32bit socket address size: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecvfrom
// Allocate IntPtr.Size extra bytes at the end of Buffer ensuring IntPtr.Size alignment, so we don't need to pin anything else.
// The following formula will extend 'size' to the alignment boundary then add IntPtr.Size more bytes.
size = (size + IntPtr.Size - 1) / IntPtr.Size * IntPtr.Size + IntPtr.Size;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int Extend(int size) => (size + IntPtr.Size - 1) / IntPtr.Size * IntPtr.Size + IntPtr.Size;

for (int i = 10; i < 30; i++) Console.Write($"{i,2} | ");
Console.WriteLine();
for (int i = 10; i < 30; i++) Console.Write($"{Extend(i),2} | ");

Will print:

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
24 | 24 | 24 | 24 | 24 | 24 | 24 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 40 | 40 | 40 | 40 | 40 |

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the old code did

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
24 | 24 | 24 | 24 | 24 | 24 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 40 | 40 | 40 | 40 | 40 | 40 |

Sensible address structure would already be aligned IMHO and we really just need to put int on boundary of its size (e.g. 4) and we need to make sure it fits to the buffer. The new code does it and now we do it only for Windows where it is used.

#endif
Buffer = new byte[size];

SocketAddressPal.SetAddressFamily(Buffer, family);
}
Expand Down Expand Up @@ -171,20 +177,23 @@ internal IPEndPoint GetIPEndPoint()
return new IPEndPoint(GetIPAddress(), GetPort());
}

#if !SYSTEM_NET_PRIMITIVES_DLL && WINDOWS
// For ReceiveFrom we need to pin address size, using reserved Buffer space.
internal void CopyAddressSizeIntoBuffer()
{
Buffer[Buffer.Length - IntPtr.Size] = unchecked((byte)(InternalSize));
Buffer[Buffer.Length - IntPtr.Size + 1] = unchecked((byte)(InternalSize >> 8));
Buffer[Buffer.Length - IntPtr.Size + 2] = unchecked((byte)(InternalSize >> 16));
Buffer[Buffer.Length - IntPtr.Size + 3] = unchecked((byte)(InternalSize >> 24));
int addressSizeOffset = GetAddressSizeOffset();
Buffer[addressSizeOffset] = unchecked((byte)(InternalSize));
Buffer[addressSizeOffset + 1] = unchecked((byte)(InternalSize >> 8));
Buffer[addressSizeOffset + 2] = unchecked((byte)(InternalSize >> 16));
Buffer[addressSizeOffset + 3] = unchecked((byte)(InternalSize >> 24));
}

// Can be called after the above method did work.
internal int GetAddressSizeOffset()
{
return Buffer.Length - IntPtr.Size;
}
#endif

public override bool Equals(object? comparand) =>
comparand is SocketAddress other &&
Expand Down