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)