// Copyright 2020 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package osimport ()var ( pollCopyFileRange = poll.CopyFileRange pollSplice = poll.Splice)func ( *File) ( io.Writer) ( int64, bool, error) { , := getPollFDAndNetwork()// TODO(panjf2000): same as File.spliceToFile.if == nil || !.IsStream || !isUnixOrTCP(string()) {return } , := .SyscallConn()if != nil {return } := .Read(func( uintptr) ( bool) { , , = poll.SendFile(, int(), 0)returntrue })if == nil { = }return , , wrapSyscallError("sendfile", )}func ( *File) ( io.Reader) ( int64, bool, error) {// Neither copy_file_range(2)/sendfile(2) nor splice(2) supports destinations opened with // O_APPEND, so don't bother to try zero-copy with these system calls. // // Visit https://man7.org/linux/man-pages/man2/copy_file_range.2.html#ERRORS and // https://man7.org/linux/man-pages/man2/sendfile.2.html#ERRORS and // https://man7.org/linux/man-pages/man2/splice.2.html#ERRORS for details.if .appendMode {return0, false, nil } , , = .copyFile()if {return }return .spliceToFile()}func ( *File) ( io.Reader) ( int64, bool, error) {var (int64 *io.LimitedReader )if , , = tryLimitedReader(); <= 0 {return0, true, nil } , := getPollFDAndNetwork()// TODO(panjf2000): run some tests to see if we should unlock the non-streams for splice. // Streams benefit the most from the splice(2), non-streams are not even supported in old kernels // where splice(2) will just return EINVAL; newer kernels support non-streams like UDP, but I really // doubt that splice(2) could help non-streams, cuz they usually send small frames respectively // and one splice call would result in one frame. // splice(2) is suitable for large data but the generation of fragments defeats its edge here. // Therefore, don't bother to try splice if the r is not a streaming descriptor.if == nil || !.IsStream {return } , , = pollSplice(&.pfd, , )if != nil { .N = - }return , , wrapSyscallError("splice", )}func ( *File) ( io.Reader) ( int64, bool, error) {var (int64 *io.LimitedReader )if , , = tryLimitedReader(); <= 0 {return0, true, nil }var *Fileswitch v := .(type) {case *File: = casefileWithoutWriteTo: = .Filedefault:return0, false, nil }if .checkValid("ReadFrom") != nil {// Avoid returning the error as we report handled as false, // leave further error handling as the responsibility of the caller.return0, false, nil } , , = pollCopyFileRange(&.pfd, &.pfd, )if != nil { .N -= }if {return , , wrapSyscallError("copy_file_range", ) }// If fd_in and fd_out refer to the same file and the source and target ranges overlap, // copy_file_range(2) just returns EINVAL error. poll.CopyFileRange will ignore that // error and act like it didn't call copy_file_range(2). Then the caller will fall back // to generic copy, which results in doubling the content in the file. // By contrast, sendfile(2) allows this kind of overlapping and works like a memmove, // in this case the file content will remain the same after copying, which is not what we want. // Thus, we just bail out here and leave it to generic copy when it's a file copying itself.if .pfd.Sysfd == .pfd.Sysfd {return0, false, nil } , := .SyscallConn()if != nil {return }// We can employ sendfile(2) when copy_file_range(2) fails to handle the copy. // sendfile(2) enabled file-to-file copying since Linux 2.6.33 and Go requires // Linux 3.2 or later, so we're good to go. // Check out https://man7.org/linux/man-pages/man2/sendfile.2.html#DESCRIPTION for more details. := .Read(func( uintptr) bool { , , = poll.SendFile(&.pfd, int(), )returntrue })if != nil { .N -= }if == nil { = }return , , wrapSyscallError("sendfile", )}// getPollFDAndNetwork tries to get the poll.FD and network type from the given interface// by expecting the underlying type of i to be the implementation of syscall.Conn// that contains a *net.rawConn.func getPollFDAndNetwork( any) (*poll.FD, poll.String) { , := .(syscall.Conn)if ! {returnnil, "" } , := .SyscallConn()if != nil {returnnil, "" } , := .(interface { () *poll.FD () poll.String })if ! {returnnil, "" }return .(), .()}func isUnixOrTCP( string) bool {switch {case"tcp", "tcp4", "tcp6", "unix":returntruedefault:returnfalse }}
The pages are generated with Goldsv0.7.3. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.