8

I am working with network buffers and streams, and Span and Memory would fit perfectly in the application requirements.

As per this question, I would like to get a Stream to accept Span as a parameter. I know that is implemented in .NET Core 2.1, but I was wondering if there's a way to get this functionality in .NET Framework as well? (I am using 4.7.1)

Something like:

Span<Byte> buffer = new Span<byte>();
stream.Read(buffer);
Tomy
  • 439
  • 1
  • 5
  • 13
  • 3
    You got a link to the code that implements it, do consider copy/pasting it. And pause a bit at what it does, note that it *still* uses a byte[] under the hood. So you are not actually ahead by using Span at all. This doesn't start to pay off until you need to slice the byte[] into smaller chunks, you can already do that now. – Hans Passant Dec 13 '18 at 11:01

3 Answers3

11

I managed to solve this by writing an extension method for the Stream class and implementing .NET Core's default behaviour for dealing with Span.

    public static int Read(this Stream thisStream, Span<byte> buffer)
    {
        byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
        try
        {
            int numRead = thisStream.Read(sharedBuffer, 0, buffer.Length);
            if ((uint)numRead > (uint)buffer.Length)
            {
                throw new IOException(SR.IO_StreamTooLong);
            }
            new Span<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
            return numRead;
        }
        finally { ArrayPool<byte>.Shared.Return(sharedBuffer); }
    }

and

    public static void Write(this Stream thisStream, ReadOnlySpan<byte> buffer)
    {
        byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
        try
        {
            buffer.CopyTo(sharedBuffer);
            thisStream.Write(sharedBuffer, 0, buffer.Length);
        }
        finally { ArrayPool<byte>.Shared.Return(sharedBuffer); }
    }
Tomy
  • 439
  • 1
  • 5
  • 13
  • 1
    In this line `throw new IOException(SR.IO_StreamTooLong);` what is SR? – Chuck Savage Apr 26 '20 at 22:53
  • Probably some custom exception class I used for handling IO exceptions. – Tomy May 01 '20 at 16:05
  • 1
    @Tomy `SR` is an `internal static class` in most .NET assemblies from Microsoft; "SR" is short for "String resources" and it's a shorthand wrapper over `ResourceManager` and is used to retrieve localized exception messages. – Dai Dec 27 '21 at 05:18
1

Unfortunately, since this functionality is not yet implemented in .Net Standard it is not included in .Net Framework.

Edit: I remember that I read from somewhere that there is pre-release NuGet package which can be used with .Net Framework

Check NuGet with System.Memory

Lloyd
  • 29,197
  • 4
  • 84
  • 98
Derviş Kayımbaşıoğlu
  • 28,492
  • 4
  • 50
  • 72
  • I did! that allows me to use Span and Memory constructs, but does not add the required overloads to Stream... or at least I couldn't figure out how to do it. – Tomy Dec 13 '18 at 11:02
  • If you feel that this answer is helpful, please consider to upvote and/or check as accepted answer – Derviş Kayımbaşıoğlu Dec 13 '18 at 11:05
  • You state "Unfortunately, since this functionality is not yet implemented in .Net Standard it is not included in .Net Framework." Note that you have it backwards. .NET Framework comes first **then** comes .NET Standard 2.0. i.e., since it's not in .NET framework, then it can't be in .NET Standard 2.0. If they were to add it into .NET Standard, it doesn't magically become supported by .NET Framework. And, in fact, you'll see that this method overload **IS** supported in .NET Standard. Version 2.1, in fact. But .NET Framework is pinned to .NET Standard 2.0. – Mike Christiansen Dec 12 '21 at 22:10
-2

The accepted solution works great!

However, in some situations where I use a shared project targeting both .NET Core and .NET Framework together, I am using both an array and a Span<T>:

var _buffer = new byte[1024*1024];
Span<Byte> buffer = _buffer;

#if NETCOREAPP3_1

stream.Read(buffer);

#else

stream.Read(_buffer);

#endif

In this way, I can avoid the small overhead of renting/copying/returning an array from ArrayPool.

Larry
  • 17,605
  • 9
  • 77
  • 106