copyZeroAlloc optimizes io.Copy by calling ReadFrom or WriteTo only when copying between os.File and net.TCPConn. If the reader has a WriteTo method, it uses WriteTo for copying; if the writer has a ReadFrom method, it uses ReadFrom for copying. If neither method is available, it gets a buffer from
(w io.Writer, r io.Reader)
| 2563 | // |
| 2564 | //nolint:dupword |
| 2565 | func copyZeroAlloc(w io.Writer, r io.Reader) (int64, error) { |
| 2566 | var readerIsFile, readerIsConn bool |
| 2567 | |
| 2568 | switch r := r.(type) { |
| 2569 | case *os.File: |
| 2570 | readerIsFile = true |
| 2571 | case *net.TCPConn: |
| 2572 | readerIsConn = true |
| 2573 | case io.WriterTo: |
| 2574 | return r.WriteTo(w) |
| 2575 | } |
| 2576 | |
| 2577 | switch w := w.(type) { |
| 2578 | case *os.File: |
| 2579 | if readerIsConn { |
| 2580 | return w.ReadFrom(r) |
| 2581 | } |
| 2582 | case *net.TCPConn: |
| 2583 | if readerIsFile { |
| 2584 | // net.WriteTo requires go1.22 or later |
| 2585 | // Benchmark tests show that on Windows, WriteTo performs |
| 2586 | // significantly better than ReadFrom. On Linux, however, |
| 2587 | // ReadFrom slightly outperforms WriteTo. When possible, |
| 2588 | // copyZeroAlloc aims to perform better than or as well |
| 2589 | // as io.Copy, so we use WriteTo whenever possible for |
| 2590 | // optimal performance. |
| 2591 | if rt, ok := r.(io.WriterTo); ok { |
| 2592 | return rt.WriteTo(w) |
| 2593 | } |
| 2594 | return w.ReadFrom(r) |
| 2595 | } |
| 2596 | case io.ReaderFrom: |
| 2597 | return w.ReadFrom(r) |
| 2598 | } |
| 2599 | |
| 2600 | vbuf := copyBufPool.Get() |
| 2601 | buf := vbuf.([]byte) |
| 2602 | n, err := copyBuffer(w, r, buf) |
| 2603 | copyBufPool.Put(vbuf) |
| 2604 | return n, err |
| 2605 | } |
| 2606 | |
| 2607 | // copyBuffer is rewritten from io.copyBuffer. We do not check if src has a |
| 2608 | // WriteTo method, if dst has a ReadFrom method, or if buf is empty. |
searching dependent graphs…