Open Jorropo opened 2 years ago
Can we get the same effect by implementing io.SectionReader.WriteTo
and io.SectionWriter.ReadFrom
?
Can we get the same effect by implementing
io.SectionReader.WriteTo
andio.SectionWriter.ReadFrom
?
Assuming the goal is not add any new interface and support concurrent operations (so no Seek + WriteTo
tricks).
To get zerocopy with that solution to work that would require io.SectionReader.WriteTo
to understand that reader
and writer
are a *os.File
and do CopyFileRange
calls.
Same for bytes.Reader
and bytes.Buffer
, io.SectionReader.WriteTo
would need to able to understand thoses two and copy from the underlying arrays.
Same for os.File
to net.TCPConn
and SendFile
calls.
What if I create a custom AES-XTS wrapper that expose a ReaderAt
/ WriterAt
managing AES-XTS encryption and writing to a an other device, are we gonna add support for that in io.SectionReader.WriteTo
? (no)
Without any standart API to expose WriterTo
+ offset + length, we are doomed to multiply adhoc solutions in potential consumers. (or be required to use solutions like importing unix
and manually firing CopyFileRange
, Splice
, SendFile
, ... calls from the buisness logic which has so many issues)
If this propsal is accepted, ultimately, *os.File
, bytes.Buffer
, bytes.Reader
, *net.TCPConn
, customAesXts.Wrapper
would all implement io.WriterToAt
and io.SectionReader.WriteTo
implementation as easy as:
func (s *SectionReader) WriteTo(w io.Writer) (int64, error) {
if wta, ok := s.r.(WriterToAt); ok {
n, err := wta.WriteToAt(w, s.off, s.limit - s.off)
r.off += n
return n, err
}
return 0, errors.ErrUnsuported
}
From code organisation perspective, this proposal is also better looking to me, because for example, the zero copy code for files would be in *os.File.WriteToAt
(where it belong, with the rest of the File code), not in *io.SectionReader.WriteTo
.
Also, this isn't the question you asked, but still
Why should this be in the std, couldn't this be wrapped in an std alternative
io
module ?
io
types (like custom net.TCPConn
, os.File
, ...) wrappers to enable thoses features.
If this is done in std, and people use io.Copy
or a friend, it magicaly works through the stack when io.Copy
try the types assertions.io
module wouldn't have access to internal/poll
, and suboptimal thread management.Recently ran into this while attempting to have multiple readers read (via io.Copy
) from the same os.File
in parallel. Wrapping the os.File
in a SectionReader
should do the trick, but that currently implies giving up on the WriterTo
fast path, and in general on any hope of zero-copy send.
Proposal
WriterToAt
ReaderToAt
(exactly the same but reader and writer are swapped)Motivations
Clean (not having to deal with FDs and
CopyFileRange
) concurrent zerocopy. On linux, at least, concurrent reads are writes are perfectly safe and sane as long as no writes overlap and no reads overlap with a write.Easly composable offset based zero copy. For example a database that use a single big file could return
*SectionReader
to objects pointing to objects on the disks. And even if multiple step later (after being wrappedio.MultiReader
call for example), zerocopy would still just work.Generalisable, other types like
bytes.Reader
could be optimised that way too.Current
WriterTo
andReaderFrom
allows similar operations if you combine them with seeking andLimitedReader
but that is not concurrent safe.The reason to use
io.Section*
types as wrappers that way is because this avoid an big (8) matrix of required interfaces:With this only 4 interfaces are required. 2 of them are already in the std already. We replace the type matrix with type assertion in the different implementations.
Related issues:
45899
io.SectionWriter
41198
errors.ErrUnsupported