span<T> and streams
Asked Answered
T

2

24

I have been reading about span for a while now, and just tried to implement it. However, while I can get span to work I cannot figure out how to get a stream to accept it like they do in the examples. Other examples show int.parse supporting spans as well but I can't find overloads or extensions that make it possible.

I have tried it in both .net standard 2.0 and .net core 2.0

Please point me in the right direction to make this work.

Code example

Span<Byte> buffer = new Span<byte>();
int bytesRead = stream.Read(buffer);
Talbert answered 28/4, 2018 at 16:22 Comment(4)
Um could you be more specific? But if you're talking about reading from a stream, try this reader.ReadBlock(charArray, 0, span.Length);Deferment
Can you edit and post some code so we can understand what you need?Autotomize
No, I am talking about using the new span<T> class to read from a stream. It is different from your typical byte array or text classes.Talbert
@Denise the question is about spans not blocks. This is a perfectly valid questionFestoonery
F
16

Span results from streams are supported in .NET Core 2.1. If you check the current source code of eg Stream you'll see it has overloads like Read(Span) that read into a Span<byte> instead of byte[], or Write(ReadOnlySpan) that can write out a ReadOnlySpan<byte> instead of a byte[], overloads that use Memory etc.

To target .NET Core 2.1, you'll have to install at least Visual Studio 2017 15.7 Preview 4 or the latest SDK for .NET Core 2.1

Festoonery answered 30/4, 2018 at 13:13 Comment(4)
I thought it would be something like that. I was just unable to find the links on any of the websites. Thanks for your help. I will probably just wait until it gets out of preview.Talbert
@ChristopherJamesWoods you don't have to. 2.1 installs side-by-side and you get to choose which runtime version to use when you create a new project. Plus, MS Build is just around the corner. There may be some interesting announcements next weekFestoonery
@PanagiotisKanavos What happens when you close and dispose of the stream? Will the Span<T> point into the void?Jumpoff
@Jumpoff the method use spans provided by the caller. They'll exist for as long as the caller needs them.Festoonery
S
6

Let's look at an example that I have handy, where the Span<T> happens to come from PipeWriter.

var bufferSpan = pipeWriter.GetSpan(count);
stream.Write( /* Darn, I need an array, because no Span<T> overloads outside Core 2.1! */ );

Try to get a Memory<T> instead of Span<T>, for which you can get the underlying array (except under some exotic circumstances).

var bufferMemory = pipeWriter.GetMemory(count); // Instead of GetSpan()
var bufferArraySegment = bufferMemory.GetUnderlyingArray();
stream.Write(bufferArraySegment.Array, bufferArraySegment.Offset, bufferArraySegment.Count); // Yay!

GetUnderlyingArray() here is a small extension method:

/// <summary>
/// Memory extensions for framework versions that do not support the new Memory overloads on various framework methods.
/// </summary>
internal static class MemoryExtensions
{
    public static ArraySegment<byte> GetUnderlyingArray(this Memory<byte> bytes) => GetUnderlyingArray((ReadOnlyMemory<byte>)bytes);
    public static ArraySegment<byte> GetUnderlyingArray(this ReadOnlyMemory<byte> bytes)
    {
        if (!MemoryMarshal.TryGetArray(bytes, out var arraySegment)) throw new NotSupportedException("This Memory does not support exposing the underlying array.");
        return arraySegment;
    }
}

All in all, this lets you use the methods with a 'modern' return type in combination with the 'old' framework overloads - without any copying. :)

Shirr answered 31/7, 2018 at 8:33 Comment(4)
This only works for read-only memory. The truth is that Stream API is quite broken: byte[] is not a substitute for "a chunk of memory". Internally, the Read(Span<T>) copies via an intermediate array.Sememe
@KubaOber I think you could never have a Stream based on a (ReadOnly)Span<byte>. Spans are stack-only types, and streams are not. The stream could outlive the span. Stream in its current form could be based on (ReadOnly)Memory<byte> at best. You'd need a stack-only stream if you wanted to wrap a span.Shirr
Of course Span and Memory go hand-in-hand, and both should be supported (supporting one must imply supporting other, otherwise it's a broken API).Sememe
Actually I see that I've been mixing things up. The question itself was about reading from a stream into a span, which either requires .NET Core 2.1+, or that you pass your own array and afterwards you get a span over that array (which is probably not what OP was looking for). My answer, I now realize, addresses writing to the stream from a Memory<byte>, in the absence of the Core 2.1 overloads that accept a span. Thirdly, there is the case of constructing a stream over a Memory<byte> of data, which does not make sense with spans, because a stack-only stream would be of limited use.Shirr

© 2022 - 2024 — McMap. All rights reserved.