Skip to content

[API Proposal]: add direct buffer access to SocketAddress #78757

@mgravell

Description

@mgravell

Background and motivation

Currently, SocketAddress has public int Size and byte this[int index] accessors; if you need the data, accessing the underlying contents (without IVTA) means using your own buffer of the .Size, and then looping to copy the data out. This is inefficient and unnecessary.

Only read access is being considered here; it is not seen as necessary (by me, at least) to support write access (which would make the internal hash optimization awkward), nor to support storage between calls - hence ReadOnlySpan<byte> seems ideal.

Originating context: Kestrel (aspnetcore) investigation into socket overheads and perf/scalability, looking into alternative underlying implementations.

API Proposal

Add a span accessor (note: naming is hard!):

public ReadOnlySpan<byte> Span => new ReadOnlySpan<byte>(Buffer, 0, InternalSize);

This enforces all the same characteristics of the existing get accessor, but allows the contents to be accessed much more efficiently.

API Usage

SocketAddress addr = ...
var bytes = addr.Span;
// use bytes

(contrast to)

SocketAddr addr = ...
var bytes = /* your choice of temporary buffer here, using addr.Size */
for (int i = 0 ; i < addr.Size; i++)
{
    bytes[i] = addr[i];
}
// use bytes

Alternative Designs

  • could be AsSpan() method instead of property getter?
  • could use CopyTo(Span<byte>), but that still forces a copy, rather than in-place direct usage

Risks

A caller could use unsafe / Unsafe to access the underlying buffer; changing the contents is already allowed (by the indexer set accessor), so that isn't by itself an additional risk - however, this would not trigger the hash-code optimization path. This, though, seems a case of "do stupid things, win stupid prizes", i.e. "don't do that". As soon as unsafe / Unsafe were used, all guarantees were gone.


Using a simple span accessor dictates that the memory has a contiguous representation; this restricts future flexibility for refactoring, although this seems pretty unlikely. But maybe a bool TryGetSpan(out ReadOnlySpan<byte>) (or TryGetBuffer, naming) would be more future-proof there? so: some hypothetical future implementation can at least say "nope" without faulting?


Not a risk, but additionally, Buffer and InternalSize are currently internal rather than private - I wonder how much code that uses those could be updated to use this new API, and perhaps benefit from bounds-check elision (a loop that uses InternalSize will not benefit, but a loop over a right-sized span: will).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions