-1

How to implement true Stream out of string and visa versa? All I see in answers on SO is this common implementation (How do I generate a stream from a string?):

public static Stream GenerateStreamFromString(string s)
{
    var stream = new MemoryStream();
    var writer = new StreamWriter(stream);
    writer.Write(s);
    writer.Flush();
    stream.Position = 0;
    return stream;
}

Obviously, this create memory pressure - a full copy in another format, not much, but still. All depends on usage of stream and size of data. If this stream is used for few starting bytes and some conditioning on those bytes - its just overkill to decode/encode entire string. So, its becoming hard to actually pipeline stream one into another and create iteration over bytes.

So, is there a way? I seem to struggle with Encoding.GetEncoder()/GetDecoder() because they require char[] array and can't accept string.

Another idea is to invert StreamWriter so it will become consumer/producer collection but its just overkill, I think.

eocron
  • 6,885
  • 1
  • 21
  • 50
  • 3
    I don't want to be ignorant - but you didn't really describe why you need this. Because maybe you could use `Span`? so you can use `"your string".AsSpan()` and then make your operations which will not put so much presure on memory – jgasiorowski Oct 14 '22 at 22:08
  • AsSpan return ReadOnlySpan of chars. Also it can't be pipelined as it is not a stream with some decoding. As described I need string->Stream conversion, not string->Span conversion. I would be happy to do later, but Im restricted with 3rd party. – eocron Oct 14 '22 at 22:10
  • 1
    Maybe this custom implementation [StringReaderStream ](https://stackoverflow.com/a/55170901/2703673) of stream from a other question can be a good start. – vernou Oct 14 '22 at 22:12
  • 1
    Thanks! Even better, found some implementation for .net5 from microsoft - https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.createtranscodingstream?view=net-5.0 – eocron Oct 14 '22 at 22:14
  • 2
    "So, is there a way?", is there a way for what? The question is very unclear. – Julian Oct 14 '22 at 22:16
  • @Julian literally my title: "String as Stream and visa versa but correct way?" – eocron Oct 14 '22 at 22:17
  • 1
    The linked question and answers are already correct... – Julian Oct 14 '22 at 22:27
  • Well correct concept of Stream is....actually an iterator over bytes, `IEnumerable>` basically. You can serialize entire iterator like they do in linked question, but this way it defeats purpose of iterator. Imagine, `Select`/`Where` from LINQ would instantly execute underlying query instead of enriching iterator. Same problem here - that's why Microsoft created TranscodingStream. – eocron Oct 14 '22 at 22:32
  • 1
    Even with answer I don’t get what is being asked… can you pledge edit the question to math whatever you answered? – Alexei Levenkov Oct 15 '22 at 00:34
  • @AlexeiLevenkov want say (I think), "correct way" is meanness. Because it can be a way to optimize the process time, memory usage, security, readability, follow a specific pattern, ... Maybe you can update the title to explicit the current way. If the "correct way" mean "memory usage", in this case you can close your question and replicate your answer in this other similar question : https://stackoverflow.com/questions/26168205/reading-string-as-a-stream-without-copying/55170901 – vernou Oct 15 '22 at 11:29

1 Answers1

1

Here is complete implementation to read string as stream, using provided encoding:

public sealed class StringStream : Stream
{
    private readonly string _input;
    private readonly Encoding _encoding;
    private readonly int _size;
    private int _position;

    public StringStream(string input, Encoding encoding)
    {
        _input = input;
        _encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
        _size = _input == null ? 0 : _encoding.GetByteCount(_input);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (_input == null)
            return 0;

        var len = Math.Min(count, _size - _position);
        if (len <= 0)
            return 0;

        var read = _encoding.GetBytes(_input, _position, len, buffer, offset);
        _position += read;
        return read;
    }

    public override void Flush()
    {
        throw new NotSupportedException();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }

    public override bool CanRead => true;
    public override bool CanSeek => false;
    public override bool CanWrite => false;
    public override long Length => _size;
    public override long Position {
        get => _position;
        set => throw new NotSupportedException();
    }
}
eocron
  • 6,885
  • 1
  • 21
  • 50