Skip to content

Commit

Permalink
net: implement WriterTo for pipes
Browse files Browse the repository at this point in the history
Implements the WriterTo interface for net pipes to avoid intermediate buffers
when copying with `io.Copy`.

Fixes golang#34624
Steven Allen committed Oct 6, 2019

Verified

This commit was signed with the committer’s verified signature.
cosmicBboy Niels Bantilan
1 parent df0dee0 commit 1bbf3a1
Showing 2 changed files with 71 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/net/pipe.go
Original file line number Diff line number Diff line change
@@ -175,6 +175,34 @@ func (p *pipe) read(b []byte) (n int, err error) {
}
}

func (p *pipe) WriteTo(w io.Writer) (n int64, err error) {
for {
switch {
case isClosedChan(p.localDone):
return n, io.ErrClosedPipe
case isClosedChan(p.remoteDone):
return n, nil
case isClosedChan(p.readDeadline.wait()):
return n, timeoutError{}
}
select {
case bw := <-p.rdRx:
nr, err := w.Write(bw)
p.rdTx <- nr
n += int64(nr)
if err != nil {
return n, err
}
case <-p.localDone:
return n, io.ErrClosedPipe
case <-p.remoteDone:
return n, nil
case <-p.readDeadline.wait():
return n, timeoutError{}
}
}
}

func (p *pipe) Write(b []byte) (int, error) {
n, err := p.write(b)
if err != nil && err != io.ErrClosedPipe {
43 changes: 43 additions & 0 deletions src/net/pipe_test.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
package net_test

import (
"bytes"
"fmt"
"io"
"net"
"testing"
@@ -47,3 +49,44 @@ func TestPipeCloseError(t *testing.T) {
t.Errorf("c2.SetDeadline() = %v, want io.ErrClosedPipe", err)
}
}

// Test WriteTo
func TestPipeCopy(t *testing.T) {
done := make(chan struct{})
r, w := net.Pipe()

var expected bytes.Buffer
go func() {
defer close(done)
defer w.Close()
for i := 0; i < 10; i++ {
n1, _ := fmt.Fprintf(&expected, "message: %d\n", i)
n2, err := fmt.Fprintf(w, "message: %d\n", i)
if err != nil {
t.Errorf("write: %v", err)
return
}
if n1 != n2 {
t.Errorf("expected to write %d bytes, wrote %d", n1, n2)
}
}
}()

var actual bytes.Buffer
n, err := io.Copy(&actual, r)
if err != nil {
t.Errorf("read: %v", err)
}

// cleanup
r.Close()
<-done

if int(n) != actual.Len() {
t.Errorf("amount copied doesn't match amount read: %d != %d", n, actual.Len())
}

if !bytes.Equal(actual.Bytes(), expected.Bytes()) {
t.Errorf("did not read expected data")
}
}

0 comments on commit 1bbf3a1

Please sign in to comment.