Closed stephentoub closed 4 years ago
No, Span is a struct.
Of course... but when slicing a String Object does the resulting Span point to the original memory of the String Object or does it point to new memory containing a partial copy of the String characters?
No, there are many use cases where a heap-based pool isn't involved. The ones I've already mentioned, others where you get native memory handed to you over interop, etc. It's of course also useful in the pooling scenario, but it's absolutely useful beyond that.
I don't understand the comment. With spans, I can write a single method that works in terms of a span, and then I can pass to it a span that represents either managed or unmanaged memory, e.g.
My question and comment where specifically about the API's listed in this issue... not about different Span use-cases in general.
when slicing a String Object does the resulting Span point to the original memory of the String Object or does it point to new memory containing a partial copy of the String characters?
The original memory of the string object.
My question and comment where specifically about the API's listed in this issue... not about different Span use-cases in general.
You wrote "in order gain value from all these overloads one would require to allocate the Span backed memory region from a pool of heap memory", which is explicitly stating that these APIs are tied to a specific Span use-case, and I'm saying that's not true. For example, to pick an example at random (pun intended), if I want 100 random bytes, today I might write:
Random r = ...;
var bytes = new byte[100];
r.Next(bytes);
but with the exposed API, I can instead write:
Random r = ...;
byte* p = stackalloc byte[100];
var bytes = new Span<byte>(p, 100);
r.Next(bytes);
and now I have my hundred bytes, but I didn't allocate any memory, and I didn't use a heap-based pool.
@stephentoub, is Buffer<T>
in the above proposal a new name for Memory<T>
? If not, can you please point me to Buffer<T>
spec? Thanks! :)
If you want a smaller set, you don't even have to stackalloc and can address of a local var/struct e.g. for 8 random bytes
Random r = ...;
ulong l;
var bytes = new Span<byte>(&l, sizeof(ulong));
r.Next(bytes);
is Buffer
in the above proposal a new name for Memory ?
Yes, the name keeps going back and forth: https://github.com/dotnet/corefxlab/blob/master/src/System.Buffers.Primitives/System/Buffers/Buffer.cs#L15
Thanks.
Random r = ...; byte* p = stackalloc byte[100]; var bytes = new Span
(p, 100); r.Next(bytes); Random r = ...; ulong l; var bytes = new Span
(&l, sizeof(ulong)); r.Next(bytes);
Those indeed show a clear use case... I suggest including within the text describing the motivation.
The original memory of the string object.
I would also suggest to state this as one of the motivations.
public String(ReadOnlySpan
value);
So I assume constructing a String with this ctor will allocate a new String object which internally points to the Span backed characters?
Actually, it would be GREAT if you could include snippets like these along each API :)
So I assume constructing a String with this ctor will allocate a new String object which internally points to the Span backed characters?
It will allocate a new string and copy the data from the span: https://github.com/dotnet/coreclr/pull/13075/files#diff-d2a641ec9b77a5ea1cc985bdec3e74efR680
It will allocate a new string and copy the data from the span: https://github.com/dotnet/coreclr/pull/13075/files#diff-d2a641ec9b77a5ea1cc985bdec3e74efR680
Then it's twice important to mention that a Span which is a result of a sliced String points to the original String memory, otherwise the use-case is not clear.
@am11 Consider subscribing to https://github.com/dotnet/corefxlab/issues/1653 to get notified when the spec gets updated/finalized.
@clrjunkie The design on Span has been full of examples and I'm not sure what you're saying is lacking. This particular thread? This thread is not to document or explain Span but to track the work to be done adding Span to existing BCL APIs.
@jnm2 I don't know what documentation you are referring to but I'm only interested in the spec which is referenced in the issue I linked to. If you had made the effort to read it you would have noticed that it is work in progress and somewhat problematic especially on the examples side. So given information about Span/Buffer is scattered all over and this particular issue is not a gantt chart but rather contains useful information about the motivations for incorporating Span within key CoreFx api's, I think what I'm saying only serves to benefit other community members who read it as many other issues link to it.
@clrjunkie
I don't know what documentation you are referring to but I'm only interested in the spec which is referenced in the issue I linked to. If you have made the effort to read it you would have noticed that it is work in progress and somewhat problematic especially on the examples side.
Yes, I did, and it the spec already answers every question you've brought up here had you made the effort to read it. I'm sorry to put it like this, but this is what it looks like from the outside:
Isn’t it true that in order gain value from all these overloads one would require to allocate the Span backed memory region from a pool of heap memory, like a pre-allocated array of value/reference types, for all inputs and outputs? If that’s the case, I would suggest highlighting this at the very beginning.
Spec: Second sentence of https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md#introduction
I can appreciate the value of having a movable window over the String characters, but wouldn't resulting Span of characters incur one allocation and one more when converting it back to String? Point being isn't this really beneficial when working with buffers (of characters in this case) from a pool?
Spec: https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md#non-allocating-substring
or using stackalloc to get the memory to use with the span.
I understand, but that's not part of above Api's contract.
Spec: allows creation from pointer https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md#api-surface
Of course... but when slicing a String Object does the resulting Span point to the original memory of the String Object or does it point to new memory containing a partial copy of the String characters?
Spec: https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md#non-allocating-substring
Etc.
I'm all for examples of use cases though, so please suggest away.
So please suggest away
@jnm2 Sorry, I'm not a native English speaker, was that supposed to be some kind of insult?
I'm sorry, not at all! "Please suggest away" means please make all the suggestions you can come up with. Let's collect all the major use cases!
Other English idioms that mean the same thing, but some could sound like opposites 😆: "go to town," "have at it," "knock yourself out." Languages are funny things!
(And thanks for confirming what I meant rather than assuming. It can be tough and it really shows a good level of maturity not always seen! Much appreciated.)
@jnm2 Ah :)) No worries! but let's see first if the ones I suggested so far get in.. they would really get a readers notice if they are presented in the context of the motivation
In this thread I saw question regarding System.Enum
and Parse/TryParse
but I have slightly diffrent question. Is it possible to have some sort of API that will allow to WriteTo/ReadFrom
binary Span<byte>
? This Span<byte>
would contain binary enum value not string representation. Such API would be useful for binary serializers.
This Span
would contain binary enum value not string representation.
Why not just cast the enum to/from its underlying integral type and use the corresponding BitConverter or similar methods with that?
True, that will work.
@stephentoub May I ask, why HashAlgorithm
and AsymmetricAlgorithm
already got a Span update and SymmetricAlgorithm
(ICryptoTransform
) didn't? Is this something you will look into? Would a addressing symmetrical crypto APIs PR be accepted?
and SymmetricAlgorithm (ICryptoTransform) didn't?
Interfaces can't be changed; adding new methods to an interface is a breaking change.
Would a addressing symmetrical crypto APIs PR be accepted?
You're welcome to open an issue with a proposal. https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/api-review-process.md
cc: @bartonjs
@stephentoub or others, sorry in advance if the question were already asked, I've searched quite some time but couldn't find for it.
I would like to know if there's an API now to initialize a Stream
based instance from a Memory<T>
instance ?
Looks like a legit use case for me, but I can't find a way to do it.
Many thanks
@nockawa, not currently publicly exposed, though you can see/copy the implementation of one here: https://github.com/dotnet/corefx/blob/master/src/Common/src/System/IO/ReadOnlyMemoryStream.cs
@stephentoub thanks, but why it's a ReadOnly version? Nothing should prevent us of dealing with a writable Memory<T>
, right?
thanks, but why it's a ReadOnly version?
Because for the internal purposes we needed this for, it would always be a ReadOnlyMemory.
Nothing should prevent us of dealing with a writable Memory
, right?
You could certainly wrap a writable stream around a Memory<T>
, but you'd of course have a length limitation on how much could be written.
With the advent of
Span<T>
andBuffer<T>
, there are a multitude of improvements we’ll want to make to types across coreclr/corefx. Some of these changes include exposingSpan<T>
/Buffer<T>
/ReadOnlySpan<T>
/ReadOnlyBuffer<T>
themselves and the associated operations on them. Other changes involve using those types internally to improve memory usage of existing code. This issue isn’t about either of those. Rather, this issue is about what new APIs we want to expose across corefx (with some implementations in coreclr/corert).A good approximation of a starting point is looking at existing APIs that work with arrays or pointers (and to some extent strings), and determining which of these should have
Span
/Buffer
-based overloads (there will almost certainly be “new” APIs we’ll want to add that don’t currently have overloads, but for the most part those can be dealt with separately and one-off). There are ~3000 such methods that exist today in the corefx reference assemblies. We’re obviously not going to add ~3000 new overloads that work withSpan
/Buffer
, nor should we. But many of these aren’t relevant for one reason or another, e.g. they’re in components considered to be legacy, they’re very unlikely to be used on hot paths where a span/buffer->array conversion would matter at all, etc.I’ve gone through the framework and identified a relatively small set of methods I believe we should start with and add together for the next release. This is dialable, of course, but I believe we need a solid set of these to represent enough mass that span/buffer permeate the stack and make sense to use in an application. All of these are cases where using a span or a buffer instead of an array makes a quantifiable difference in allocation, and thus can have a measurable impact on the overall memory profile of a consuming application, contributing to an overall improvement in performance.
System.BitConverter
BitConverter is used to convert between primitive types and bytes, but the current APIs force an unfortunate amount of allocation due to working with byte[]s. We can help to avoid much of that by adding overloads that work with
Span<byte>
, addingCopyBytes
methods instead ofGetBytes
methods, and addingTo*
overloads that acceptReadOnlySpan<byte>
instead of acceptingbyte[]
.EDIT 7/18/2017: Updated based on API review. Separated out into https://github.com/dotnet/corefx/issues/22355 for implementation.
System.Convert
As with BitConverter, the Convert class is also used to convert arrays. Most of the members are about converting from individual primitives to other individual primitives, but several work with arrays, in particular those for working with Base64 data. We should add the following methods:
Separated out as https://github.com/dotnet/corefx/issues/22417 for implementation.
System.Random
The Random class provides a NextBytes method that takes a byte[]. In many situations, that’s fine, but in some you’d like to be able to get an arbitrary amount of random data without having to allocate such an array, and we can do that with spans. For example, here’s a case where we’re getting some random data only to then want a Base64 string from it: https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,366
Separated out into https://github.com/dotnet/corefx/issues/22356 for implementation.
Primitive Parse methods
Related to BitConverter and Convert, it’s very common to want to parse primitive values out of strings. Today, that unfortunately often involves creating substrings representing the exact piece of text, and then passing it to a string-based TryParse method. Similarly, it's common to convert primitives to strings via a method like ToString. Instead, I suggest we add the following:
Separated out as https://github.com/dotnet/corefx/issues/22403 for implementation.
Then we should also support parsing a few common types out of
ReadOnlySpan<char>
:EDIT 7/18/2017: Updated per API review DateTime{Offset} separated out as https://github.com/dotnet/corefx/issues/22358 for implementation. TimeSpan separated out as https://github.com/dotnet/corefx/issues/22375 for implementation. Version separated out as https://github.com/dotnet/corefx/issues/22376 for implementation.
System.Guid
Guids are often constructed from byte[]s, and these byte[]s are often new’d up, filled in, and then provided to the Guid, e.g. https://referencesource.microsoft.com/#mscorlib/system/guid.cs,b622ef5f6b76c10a,references We can avoid such allocations with a Span ctor, with the call sites instead creating a Span from 16 bytes of stack memory. Guids are also frequently converted to byte[]s to be output, e.g.
https://referencesource.microsoft.com/#mscorlib/system/guid.cs,94f5d8dabbf0dbcc,references
and we can again avoid those temporary allocations by supporting copying the Guid’s data to a span:
EDIT 7/18/2017: Updated per API review Separated out as https://github.com/dotnet/corefx/issues/22377 for implementation.
System.String
It’ll be very common to create strings from spans. We should have a ctor for doing so:
Separated out as https://github.com/dotnet/corefx/issues/22378 for implementation.
In addition to the new ctor on String, we should also expose an additional Create method (not a ctor so as to support a generic method argument). One of the difficulties today with String being immutable is it’s more expensive for developers to create Strings with custom logic, e.g. filling a char[] and then creating a String from that. To work around that expense, some developers have taken to mutating strings, which is very much frowned upon from a BCL perspective, e.g. by using the String(char, int) ctor to create the string object, then using unsafe code to mutate it, and then handing that back. We could handle such patterns by adding a method like this:
Separated out as https://github.com/dotnet/corefx/issues/22380 for implementation.
The value of overloads like this is amplified when you start using them together. For example, here’s an example of creating a random string: https://referencesource.microsoft.com/#System.Web.Mobile/UI/MobileControls/Adapters/ChtmlTextBoxAdapter.cs,62 This incurs the allocation of a byte[] to pass to Random, a char[] as a temporary from which to create the string, and then creating the string from that char[]; unnecessary copies and allocations.
Finally, we may also want to provide string.Format support for
ReadOnlyBuffer<char>
as an argument; although passing it as an object will box it, string.Format could write it to the generated string without needing an intermediary string created, so a smaller allocation and avoiding an extra copy.System.IO.Stream
EDIT 7/18/2017: Updated per API review. Separated out as https://github.com/dotnet/corefx/issues/22381 for implementation.
The base
{ReadOnly}Buffer
-accepting methods can use TryGetArray to access a wrapped array if there is one, then delegating to the existing array-based overloads. If the buffer doesn’t wrap an array, then it can get a temporary array from ArrayPool, delegate and copy (for reads) or copy and delegate (for writes).The base
Span
-accepting methods can do the ArrayPool/delegation approach in all cases.We’ll then need to override these methods on all relevant streams: FileStream, NetworkStream, SslStream, DeflateStream, CryptoStream, MemoryStream, UnmanagedMemoryStream, PipeStream, NullStream, etc. to provide more efficient
Span
/Buffer
-based implementations, which they should all be able to do. In a few corner cases, there are streams where the synchronous methods are actually implemented as wrappers for the asynchronous ones; in such cases, we may be forced to live with the ArrayPool/copy solution. However, in such cases, the synchronous implementation is already relatively poor, incurring additional costs (e.g. allocations, blocking a thread, etc.), and they’re that way in part because synchronous usage of such streams is discouraged and thus these haven’t been very optimized, so the extra overhead in these cases is minimal.Once these additional methods exist, we’ll want to use them in a variety of places in implementation around corefx, e.g. https://github.com/dotnet/corefx/blob/d6b11250b5113664dd3701c25bdf9addfacae9cc/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs#L1140 as various pieces of code can benefit not only from the use of
Span
/Buffer
, but also from the async methods returningValueTask<T>
instead ofTask<T>
, and the corresponding reduced allocations for ReadAsync calls that complete synchronously.System.IO.BufferStream and System.IO.ReadOnlyBufferStream
Just as we have a MemoryStream that works with
byte[]
and an UnmanagedMemoryStream that works with abyte*
and a length, we need to support treatingBuffer<byte>
andReadOnlyBuffer<byte>
as streams. It’s possible we could get away with reimplementingMemoryStream
on top ofBuffer<byte>
, but more than likely this would introduce regressions for at least some existing use cases. Unless demonstrated otherwise, we will likely want to have two new stream types for these specific types:The name of BufferStream is unfortunately close to that of BufferedStream, and they mean very different things, but I’m not sure that’s important enough to consider a less meaningful name. Separated out as https://github.com/dotnet/corefx/issues/22404 for discussion and implementation.
Optional: It’s also unfortunate that all of these various memory-based streams don’t share a common base type or interface; we may want to add such a thing, e.g. an interface that anything which wraps a
Buffer<T>
can implement… then for example code that uses Streams and has optimizations when working directly with the underlying data can query for the interface and special-case when the underlyingBuffer<T>
can be accessed, e.g.We could implement this not only on
BufferStream
andReadOnlyBufferStream
, but also onMemoryStream
,UnmanagedMemoryStream
, and even on non-streams, basically anything that can hand out a representation of its internals as buffers. Separated out as https://github.com/dotnet/corefx/issues/22404 for discussion and implementation.System.IO.TextReader and System.IO.TextWriter
As with streams, we should add the relevant base virtuals for working with spans/buffers:
We’ll then also need to override these appropriately on our derived types, e.g. StreamReader, StreamWriter, StringReader, etc.
Separated out as https://github.com/dotnet/corefx/issues/22406 for implementation.
System.IO.BinaryReader and System.IO.BinaryWriter
As with TextReader/Writer, we also want to enable the existing BinaryReader/Writer to work with spans:
The base implementations of these can work with the corresponding new methods on Stream. Separated out as https://github.com/dotnet/corefx/issues/22428 and https://github.com/dotnet/corefx/issues/22429 for implementation.
System.IO.File
EDIT 6/26/2017: For now I've removed these File methods, as it's unclear to me at this point that they actually meet the core need. If you're repeatedly reading and pass a span that's too small, you're going to need to read again after handling the read data, but with these APIs as defined, each read results in needing to open and the close the file again; similarly if you're using the write APIs to write a file in pieces. At that point you're better off just using FileStream and reading/writing spans and buffers. We should look to add helpers here when we can figure out the right helpers at the right level of abstraction, e.g. should we expose the ability to just create a SafeFileHandle and then have these helpers operate on SafeFileHandle rather than on a string path?
The File class provides helpers for reading/writing data from/to files. Today the various “read/write data as bytes” functions work with byte[]s, which mean allocating potentially very large byte[]s to store the data to be written or that’s read. This can be made much more efficient with spans, allowing the buffers to be pooled (in particular for file reads).System.Text.StringBuilder
StringBuilder is at the core of lots of manipulation of chars, and so it’s a natural place to want to work with
Span<char>
andReadOnlySpan<char>
. At a minimum we should add the following APIs to make it easy to get data in and out of a StringBuilder without unnecessary allocation:Separated out as https://github.com/dotnet/corefx/issues/22430.
In addition to these, we’ll also want to consider a mechanism for exposing the one or more buffers StringBuilder internally maintains, allowing them to be accessed as
ReadOnlySpan<char>
. This is covered separately by dotnet/runtime#22371.System.Text.Encoding
The Encoding class already exposes a lot of methods for converting between chars/strings and bytes, and it includes overloads for both arrays and pointers. While it might seem onerous to add an additional set for spans, the number of new overloads needed is actually fairly small, and we can provide reasonable default base implementations in terms of the existing pointer-based overloads. These methods could potentially have optimized derived overrides, but more generally they will help the platform to have a consistent feel, avoiding the need for devs with spans to use unsafe code to access the pointer-based methods.
Separated out as https://github.com/dotnet/corefx/issues/22431.
System.Numerics
Separated out in https://github.com/dotnet/corefx/issues/22401 for implementation.
Even with BigInteger being a less-commonly-used type, there are still places even in our own code where this would be helpful: https://source.dot.net/#System.Security.Cryptography.X509Certificates/Common/System/Security/Cryptography/DerSequenceReader.cs,206 In fact, BigInteger’s implementation itself could benefit from this, such as with parsing: https://referencesource.microsoft.com/#System.Numerics/System/Numerics/BigNumber.cs,411
System.Net.IPAddress
It’s very common to create IPAddresses from data gotten off the network, and in high-volume scenarios. Internally we try to avoid creating
byte[]
s to pass to IPAddress when constructing them, such as by using an internal pointer-based ctor, but outside code doesn’t have that luxury. We should make it possible to go to and from IPAddress without additional allocation:Even internally this will be useful in cases like these: https://source.dot.net/#System.Net.NetworkInformation/Common/System/Net/SocketAddress.cs,135 https://source.dot.net/#System.Net.NameResolution/Common/System/Net/Internals/IPAddressExtensions.cs,21
EDIT 7/25/2017: Updated based on API review Separated out as https://github.com/dotnet/corefx/issues/22607 for implementation.
System.Net.Sockets
This is one of the more impactful areas for spans and buffers, and having these methods will be a welcome addition to the Socket family.
Today, there are lots of existing methods on sockets, e.g. an overload for sync vs async with the APM pattern vs async with Task vs async with SocketAsyncEventArgs, and overload for Send vs Receive vs SendTo vs ReceiveMessageFrom vs… etc. I do not think we should add an entire new set of span/buffer-based methods, and instead we should start with the most impactful. To me, that means adding the following two synchronous and two Task-based asynchronous overloads for sending and receiving:
Note that I’ve put the ReceiveAsync and SendAsync overloads on the SocketTaskExtensions class as that’s where the existing overloads live. We could choose to instead make these new overloads instance methods on Socket.
In addition, the highest-performing set of APIs with sockets are based on SocketAsyncEventArgs. To support those, we should add the following:
The implementation currently stores a
byte[]
buffer. We can change that to store aBuffer<byte>
instead, and just have the existingSetBuffer(byte[])
overload wrap thebyte[]
in aBuffer<byte>
. There is an existingBuffer { get; }
property as well. We can change that to just use TryGetArray on the internal buffer and return it if it exists, or else null. TheGetBuffer
method is then there to support getting the setBuffer<byte>
, in case the supplied buffer wrapped something other than an array.EDIT 7/25/2017: Updated based on API review Separated out as https://github.com/dotnet/corefx/issues/22608 for implementation
System.Net.WebSockets.WebSocket
Similar to Socket is WebSocket, in that it’s desirable to be able to use these APIs with finer-grain control over allocations than is currently easy or possible, and in high-throughput situations. We should add the following members:
Note that ReceiveAsync is returning a ValueTask rather than a Task (to avoid that allocation in the case where the operation can complete synchronously), and it’s wrapping a ValueWebSocketReceiveResult instead of a WebSocketReceiveResult. The latter is the type that currently exists, but is a class. I’m suggesting we also add the following struct-based version, so that receives can be made to be entirely allocation-free in the case of synchronous completion, and significantly less allocating even for async completion.
EDIT 7/25/2017: Updated per API review Separated out as https://github.com/dotnet/corefx/issues/22610 for implementation
System.Net.Http
This area still needs more thought. My current thinking is at a minimum we add the following:
The GetBytesAsync overload avoids the need for allocating a potentially large byte[] to store the result, and the ReadOnlyBufferContent makes it easy to upload
ReadOnlyBuffer<byte>
s rather than justbyte[]
s.There is potentially more that can be done here, but I suggest we hold off on that until we investigate more deeply to understand what we’d need to plumb throughout the system. HttpClient is itself a relatively small wrapper on top of HttpClientHandler, of which there are multiple implementations (ours and others’), and to plumb a buffer all the way down through would involve new APIs and implementation in HttpClientHandler implementations. Definitely something to investigate.
Separated out as https://github.com/dotnet/corefx/issues/22612 for implementation (ReadOnlyBufferContent... we decided against the GetBytesAsync helper for now in 7/25/2017 API review)
System.Security.Cryptography namespace
Several hashing-related types in System.Security.Cryptography would benefit from being able to work with data without allocating potentially large
byte[]
s. One is HashAlgorithm, which has several methods for processing an inputbyte[]
and allocating/filling an outputbyte[]
:EDIT 7/25/2017: Updated per API review Separated out as https://github.com/dotnet/corefx/issues/22613 for implementation
Similarly, several overloads would be beneficial on IncrementalHash:
EDIT 7/25/2017: Updated per API review Separated out as https://github.com/dotnet/corefx/issues/22614 for implementation
Similarly, several encryption/signing related types would benefit from avoiding such
byte[]
allocations:EDIT 7/25/2017: Update per API review Separated out of https://github.com/dotnet/corefx/issues/22615 for implementation
One other place where we’d want to be able to use span is
ICryptoTransform
. Lots of APIs, mainly CreateEncryptor and CreateDecryptor, methods return ICryptoTransform, and it has one it a TransformBlock and TransformFinalBlock method that works withbyte[]
. We’d really like support in terms ofReadOnlySpan<byte>
andSpan<byte>
. I see four options here:ISpanCryptoTransform
interface, and a second set of differently-named CreateEncryptor/Decryptor methods for creating instances of these.ISpanCryptoTransform
interface that’s also implemented by our implementations, and have consuming code query for the interface and use it if it exists.EDIT 7/25/2017: Decision: 4 if/when the feature exists, and 1 until then.
Other namespaces
There are some other namespaces that could likely benefit from Span/Buffer, but we should probably handle separately from this initial push:
Span<T>
? Callback-based methods for exposing the internal storage via aReadOnlySpan<T>
(if that makes sense for the relevant collection)? This needs more thought.T[]
has an implicit operator to aSpan<T>
, we should consider adding anImmutableArray<T>
operator for converting to aReadOnlySpan<T>
.Known Open Issues
There are several cross-cutting open issues that apply to many of these APIs:
ReadOnlySpan<T>
andSpan<T>
, and “buffer” forReadOnlyBuffer<T>
andBuffer<T>
? What if the existing overload uses a different name, like “input” or “b”? What about cases where currently there’s a single argument (e.g. “input”) and then returns an array, and the new overload will have two span args, one input and one output?Next Steps