github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/common/bufio/vectorised_unix.go (about) 1 //go:build !windows 2 3 package bufio 4 5 import ( 6 "os" 7 "sync" 8 "unsafe" 9 10 "github.com/sagernet/sing/common/buf" 11 M "github.com/sagernet/sing/common/metadata" 12 13 "golang.org/x/sys/unix" 14 ) 15 16 type syscallVectorisedWriterFields struct { 17 access sync.Mutex 18 iovecList *[]unix.Iovec 19 } 20 21 func (w *SyscallVectorisedWriter) WriteVectorised(buffers []*buf.Buffer) error { 22 w.access.Lock() 23 defer w.access.Unlock() 24 defer buf.ReleaseMulti(buffers) 25 var iovecList []unix.Iovec 26 if w.iovecList != nil { 27 iovecList = *w.iovecList 28 } 29 iovecList = iovecList[:0] 30 for index, buffer := range buffers { 31 iovecList = append(iovecList, unix.Iovec{Base: &buffer.Bytes()[0]}) 32 iovecList[index].SetLen(buffer.Len()) 33 } 34 if w.iovecList == nil { 35 w.iovecList = new([]unix.Iovec) 36 } 37 *w.iovecList = iovecList // cache 38 var innerErr unix.Errno 39 err := w.rawConn.Write(func(fd uintptr) (done bool) { 40 //nolint:staticcheck 41 //goland:noinspection GoDeprecation 42 _, _, innerErr = unix.Syscall(unix.SYS_WRITEV, fd, uintptr(unsafe.Pointer(&iovecList[0])), uintptr(len(iovecList))) 43 return innerErr != unix.EAGAIN && innerErr != unix.EWOULDBLOCK 44 }) 45 if innerErr != 0 { 46 err = os.NewSyscallError("SYS_WRITEV", innerErr) 47 } 48 for index := range iovecList { 49 iovecList[index] = unix.Iovec{} 50 } 51 return err 52 } 53 54 func (w *SyscallVectorisedPacketWriter) WriteVectorisedPacket(buffers []*buf.Buffer, destination M.Socksaddr) error { 55 w.access.Lock() 56 defer w.access.Unlock() 57 defer buf.ReleaseMulti(buffers) 58 var iovecList []unix.Iovec 59 if w.iovecList != nil { 60 iovecList = *w.iovecList 61 } 62 iovecList = iovecList[:0] 63 for index, buffer := range buffers { 64 iovecList = append(iovecList, unix.Iovec{Base: &buffer.Bytes()[0]}) 65 iovecList[index].SetLen(buffer.Len()) 66 } 67 if w.iovecList == nil { 68 w.iovecList = new([]unix.Iovec) 69 } 70 *w.iovecList = iovecList // cache 71 var innerErr error 72 err := w.rawConn.Write(func(fd uintptr) (done bool) { 73 var msg unix.Msghdr 74 name, nameLen := ToSockaddr(destination.AddrPort()) 75 msg.Name = (*byte)(name) 76 msg.Namelen = nameLen 77 if len(iovecList) > 0 { 78 msg.Iov = &iovecList[0] 79 msg.SetIovlen(len(iovecList)) 80 } 81 _, innerErr = sendmsg(int(fd), &msg, 0) 82 return innerErr != unix.EAGAIN && innerErr != unix.EWOULDBLOCK 83 }) 84 if innerErr != nil { 85 err = innerErr 86 } 87 for index := range iovecList { 88 iovecList[index] = unix.Iovec{} 89 } 90 return err 91 } 92 93 //go:linkname sendmsg golang.org/x/sys/unix.sendmsg 94 func sendmsg(s int, msg *unix.Msghdr, flags int) (n int, err error)