You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I've been using Stream APIs for some time now and in many cases memory is often copied when reading, and for that reason there are internal functions that solve these shortcomings, although some internal functions solve this, but they are not available to us as end users.
I would like to talk about the relationship between BinaryReader and MemoryStream in details.
BinaryReader
As for BinaryReader, it is a very useful class that could also be extended by child classes. Did i say could? So it's not possible? As for C# rules, sure, but if I want to add or override an existing write method, I end up with very disgusting performance, because it's fast natively, it's only thanks to internal workarounds.
Yes, I can create my own MemoryStream and my own BinaryWriter/BinaryReader, but then it loses its compatibility with both 3rd-party and native libraries.
Solution
The answer to these things would be two new methods for the base Steam class.
void virtual Span<byte> DirectGet(int length, bool moveCursor = true)
Both of these methods should all round speed up the old stream APIs and avoid unnecessary heap memory allocation and unnecessary memory copying.
API Proposal
Proposal Concept
namespaceSystem.IO;publicabstractclassStream{publicvirtualboolCanDirectRead{get=>false;}publicvirtualReadOnlySpan<byte>DirectRead(intlength){thrownewNotImplementedException();}publicvirtualSpan<byte>DirectGet(intlength,boolmoveCursor){thrownewNotImplementedException();}}publicclassMemoryStream:Stream{publicoverrideboolCanDirectRead{get=>true;}publicoverrideReadOnlySpan<byte>DirectRead(intlength){EnsureNotClosed();intorigPos=_position;intnewPos=origPos+count;if((uint)newPos>(uint)_length){_position=_length;ThrowHelper.ThrowEndOfFileException();}varspan=newReadOnlySpan<byte>(_buffer,origPos,count);_position=newPos;returnspan;}publicoverrideSpan<byte>DirectGet(intlength,boolmoveCursor=true){EnsureNotClosed();// if(!CanWrite) throw ... intorigPos=_position;intnewPos=origPos+count;if((uint)newPos>(uint)_length){_position=_length;ThrowHelper.ThrowEndOfFileException();}varspan=newSpan<byte>(_buffer,origPos,count);if(moveCursor)_position=newPos;returnspan;}}publicclassBinaryReader{//Internal re-implementationprivateReadOnlySpan<byte>InternalRead(Span<byte>buffer){Debug.Assert(buffer.Length!=1,"length of 1 should use ReadByte.");if(_stream.CanDirectRead){/* Previose method // read directly from MemoryStream buffer Debug.Assert(_stream is MemoryStream); return Unsafe.As<MemoryStream>(_stream).InternalReadSpan(buffer.Length); */return_stream.DirectRead(buffer.Length);}else{ThrowIfDisposed();_stream.ReadExactly(buffer);returnbuffer;}}}
API Usage
Pseudo Concept Example - Custom Binary Reader
//Initialize own exteneded implementation of BinaryReaderusingMyBinaryReaderreader=newMyBinaryReader(newMemoryStream(packet));//Read int in my own way!!!intmyInt=reader.ReadInt32BigEndian();
Pseudo Concept Example - Custom MemoryStream
//Initialize BinaryReader with own MemoryStream implementationusingBinaryReaderreader=newBinaryReader(newMyMemoryStream(packet));//Read int really fast!!!intmyInt=reader.ReadInt32();
Alternative Designs
The examples given above solve two birds with one stone, both the acceleration of reading from MemoryStream and the extensibility of BineryReader.
Another possibility would be to publish the internal method InternalRead in the BineryReader class, but that would not change anything, since the binary reader would also read slowly from memory streams other than the native one. (Its Hardcoded)
Also keep in mind that descendant of the MemoryStream class is not marked as MemoryStream for BinaryReader as well, so that results in slow reading from descendants of MemoryStream class.
Risks
The method name can be misleading for beginners, and for most stream implementations it will throw an exception NotImplementedException.
The text was updated successfully, but these errors were encountered:
Background and motivation
I've been using Stream APIs for some time now and in many cases memory is often copied when reading, and for that reason there are internal functions that solve these shortcomings, although some internal functions solve this, but they are not available to us as end users.
I would like to talk about the relationship between BinaryReader and MemoryStream in details.
BinaryReader
As for BinaryReader, it is a very useful class that could also be extended by child classes. Did i say could? So it's not possible? As for C# rules, sure, but if I want to add or override an existing write method, I end up with very disgusting performance, because it's fast natively, it's only thanks to internal workarounds.
Resources And Links
Yes, I can create my own MemoryStream and my own BinaryWriter/BinaryReader, but then it loses its compatibility with both 3rd-party and native libraries.
Solution
The answer to these things would be two new methods for the base Steam class.
void virtual ReadOnlySpan<byte> DirectRead(int length)
void virtual Span<byte> DirectGet(int length, bool moveCursor = true)
Both of these methods should all round speed up the old stream APIs and avoid unnecessary heap memory allocation and unnecessary memory copying.
API Proposal
Proposal Concept
API Usage
Pseudo Concept Example - Custom Binary Reader
Pseudo Concept Example - Custom MemoryStream
Alternative Designs
The examples given above solve two birds with one stone, both the acceleration of reading from MemoryStream and the extensibility of BineryReader.
Another possibility would be to publish the internal method InternalRead in the BineryReader class, but that would not change anything, since the binary reader would also read slowly from memory streams other than the native one. (Its Hardcoded)
Also keep in mind that descendant of the MemoryStream class is not marked as MemoryStream for BinaryReader as well, so that results in slow reading from descendants of MemoryStream class.
Risks
The method name can be misleading for beginners, and for most stream implementations it will throw an exception
NotImplementedException
.The text was updated successfully, but these errors were encountered: