Source file
src/os/zero_copy_linux.go
Documentation: os
1
2
3
4
5 package os
6
7 import (
8 "internal/poll"
9 "io"
10 "syscall"
11 )
12
13 var (
14 pollCopyFileRange = poll.CopyFileRange
15 pollSplice = poll.Splice
16 pollSendFile = poll.SendFile
17 )
18
19 func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) {
20 pfd, network := getPollFDAndNetwork(w)
21
22 if pfd == nil || !pfd.IsStream || !isUnixOrTCP(string(network)) {
23 return
24 }
25
26 sc, err := f.SyscallConn()
27 if err != nil {
28 return
29 }
30
31 rerr := sc.Read(func(fd uintptr) (done bool) {
32 written, err, handled = pollSendFile(pfd, int(fd), 1<<63-1)
33 return true
34 })
35
36 if err == nil {
37 err = rerr
38 }
39
40 return written, handled, wrapSyscallError("sendfile", err)
41 }
42
43 func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) {
44
45
46
47
48
49 if f.appendMode {
50 return 0, false, nil
51 }
52
53 written, handled, err = f.copyFileRange(r)
54 if handled {
55 return
56 }
57 return f.spliceToFile(r)
58 }
59
60 func (f *File) spliceToFile(r io.Reader) (written int64, handled bool, err error) {
61 var (
62 remain int64
63 lr *io.LimitedReader
64 )
65 if lr, r, remain = tryLimitedReader(r); remain <= 0 {
66 return 0, true, nil
67 }
68
69 pfd, _ := getPollFDAndNetwork(r)
70
71
72
73
74
75
76
77 if pfd == nil || !pfd.IsStream {
78 return
79 }
80
81 var syscallName string
82 written, handled, syscallName, err = pollSplice(&f.pfd, pfd, remain)
83
84 if lr != nil {
85 lr.N = remain - written
86 }
87
88 return written, handled, wrapSyscallError(syscallName, err)
89 }
90
91 func (f *File) copyFileRange(r io.Reader) (written int64, handled bool, err error) {
92 var (
93 remain int64
94 lr *io.LimitedReader
95 )
96 if lr, r, remain = tryLimitedReader(r); remain <= 0 {
97 return 0, true, nil
98 }
99
100 var src *File
101 switch v := r.(type) {
102 case *File:
103 src = v
104 case fileWithoutWriteTo:
105 src = v.File
106 default:
107 return 0, false, nil
108 }
109
110 if src.checkValid("ReadFrom") != nil {
111
112
113 return 0, false, nil
114 }
115
116 written, handled, err = pollCopyFileRange(&f.pfd, &src.pfd, remain)
117 if lr != nil {
118 lr.N -= written
119 }
120 return written, handled, wrapSyscallError("copy_file_range", err)
121 }
122
123
124
125
126 func getPollFDAndNetwork(i any) (*poll.FD, poll.String) {
127 sc, ok := i.(syscall.Conn)
128 if !ok {
129 return nil, ""
130 }
131 rc, err := sc.SyscallConn()
132 if err != nil {
133 return nil, ""
134 }
135 irc, ok := rc.(interface {
136 PollFD() *poll.FD
137 Network() poll.String
138 })
139 if !ok {
140 return nil, ""
141 }
142 return irc.PollFD(), irc.Network()
143 }
144
145
146
147
148 func tryLimitedReader(r io.Reader) (*io.LimitedReader, io.Reader, int64) {
149 var remain int64 = 1<<63 - 1
150
151 lr, ok := r.(*io.LimitedReader)
152 if !ok {
153 return nil, r, remain
154 }
155
156 remain = lr.N
157 return lr, lr.R, remain
158 }
159
160 func isUnixOrTCP(network string) bool {
161 switch network {
162 case "tcp", "tcp4", "tcp6", "unix":
163 return true
164 default:
165 return false
166 }
167 }
168
View as plain text