// 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)// wrapSyscallError takes an error and a syscall name. If the error is// a syscall.Errno, it wraps it in an os.SyscallError using the syscall name.func wrapSyscallError( string, error) error {if , := .(syscall.Errno); { = NewSyscallError(, ) }return}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(), 1<<63-1)returntrue })if == nil { = }return , , wrapSyscallError("sendfile", )}func ( *File) ( io.Reader) ( int64, bool, error) {// Neither copy_file_range(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/splice.2.html#ERRORS for details.if .appendMode {return0, false, nil } , , = .copyFileRange()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 -= }return , , wrapSyscallError("copy_file_range", )}// 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 .(), .()}// tryLimitedReader tries to assert the io.Reader to io.LimitedReader, it returns the io.LimitedReader,// the underlying io.Reader and the remaining amount of bytes if the assertion succeeds,// otherwise it just returns the original io.Reader and the theoretical unlimited remaining amount of bytes.func tryLimitedReader( io.Reader) (*io.LimitedReader, io.Reader, int64) {varint64 = 1<<63 - 1// by default, copy until EOF , := .(*io.LimitedReader)if ! {returnnil, , } = .Nreturn , .R, }func isUnixOrTCP( string) bool {switch {case"tcp", "tcp4", "tcp6", "unix":returntruedefault:returnfalse }}
The pages are generated with Goldsv0.7.0-preview. (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.