mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 20:07:38 +03:00
Add vectorised interface
This commit is contained in:
parent
13f394e202
commit
05f9836bff
12 changed files with 371 additions and 13 deletions
|
@ -321,6 +321,12 @@ func (b *Buffer) Release() {
|
|||
*b = Buffer{closed: true}
|
||||
}
|
||||
|
||||
func ReleaseMulti(buffers []*Buffer) {
|
||||
for _, buffer := range buffers {
|
||||
buffer.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) Cut(start int, end int) *Buffer {
|
||||
b.start += start
|
||||
b.end = len(b.data) - end
|
||||
|
|
|
@ -38,3 +38,17 @@ func WriteTo(writer N.PacketWriter, buffer *buf.Buffer, addr net.Addr) (n int, e
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
func WriteVectorised(writer N.VectorisedWriter, data [][]byte) (n int, err error) {
|
||||
var dataLen int
|
||||
buffers := make([]*buf.Buffer, 0, len(data))
|
||||
for _, p := range data {
|
||||
dataLen += len(p)
|
||||
buffers = append(buffers, buf.As(p))
|
||||
}
|
||||
err = writer.WriteVectorised(buffers)
|
||||
if err == nil {
|
||||
n = dataLen
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
109
common/bufio/vectorised.go
Normal file
109
common/bufio/vectorised.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package bufio
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func CreateVectorisedWriter(writer any) (N.VectorisedWriter, bool) {
|
||||
switch w := writer.(type) {
|
||||
case N.VectorisedWriter:
|
||||
return w, true
|
||||
case *net.TCPConn:
|
||||
return &NetVectorisedWriterWrapper{w}, true
|
||||
case *net.UDPConn:
|
||||
return &NetVectorisedWriterWrapper{w}, true
|
||||
case *net.IPConn:
|
||||
return &NetVectorisedWriterWrapper{w}, true
|
||||
case *net.UnixConn:
|
||||
return &NetVectorisedWriterWrapper{w}, true
|
||||
case syscall.Conn:
|
||||
rawConn, err := w.SyscallConn()
|
||||
if err != nil {
|
||||
return &SyscallVectorisedWriter{writer, rawConn}, true
|
||||
}
|
||||
case syscall.RawConn:
|
||||
return &SyscallVectorisedWriter{writer, w}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func CreateVectorisedPacketWriter(writer any) (N.VectorisedPacketWriter, bool) {
|
||||
switch w := writer.(type) {
|
||||
case N.VectorisedPacketWriter:
|
||||
return w, true
|
||||
case syscall.Conn:
|
||||
rawConn, err := w.SyscallConn()
|
||||
if err != nil {
|
||||
return &SyscallVectorisedPacketWriter{writer, rawConn}, true
|
||||
}
|
||||
case syscall.RawConn:
|
||||
return &SyscallVectorisedPacketWriter{writer, w}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var _ N.VectorisedWriter = (*NetVectorisedWriterWrapper)(nil)
|
||||
|
||||
type NetVectorisedWriterWrapper struct {
|
||||
upstream io.Writer
|
||||
}
|
||||
|
||||
func (w *NetVectorisedWriterWrapper) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
defer buf.ReleaseMulti(buffers)
|
||||
netBuffers := make(net.Buffers, 0, len(buffers))
|
||||
for _, buffer := range buffers {
|
||||
netBuffers = append(netBuffers, buffer.Bytes())
|
||||
}
|
||||
return common.Error(netBuffers.WriteTo(w.upstream))
|
||||
}
|
||||
|
||||
func (w *NetVectorisedWriterWrapper) Upstream() any {
|
||||
return w.upstream
|
||||
}
|
||||
|
||||
func (w *NetVectorisedWriterWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var _ N.VectorisedWriter = (*SyscallVectorisedWriter)(nil)
|
||||
|
||||
type SyscallVectorisedWriter struct {
|
||||
upstream any
|
||||
rawConn syscall.RawConn
|
||||
}
|
||||
|
||||
func (w *SyscallVectorisedWriter) Upstream() any {
|
||||
return w.upstream
|
||||
}
|
||||
|
||||
func (w *SyscallVectorisedWriter) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var _ N.VectorisedPacketWriter = (*SyscallVectorisedPacketWriter)(nil)
|
||||
|
||||
type SyscallVectorisedPacketWriter struct {
|
||||
upstream any
|
||||
rawConn syscall.RawConn
|
||||
}
|
||||
|
||||
func (w *SyscallVectorisedPacketWriter) Upstream() any {
|
||||
return w.upstream
|
||||
}
|
||||
|
||||
var _ N.VectorisedPacketWriter = (*UnbindVectorisedPacketWriter)(nil)
|
||||
|
||||
type UnbindVectorisedPacketWriter struct {
|
||||
N.VectorisedWriter
|
||||
}
|
||||
|
||||
func (w *UnbindVectorisedPacketWriter) WriteVectorisedPacket(buffers []*buf.Buffer, _ M.Socksaddr) error {
|
||||
return w.WriteVectorised(buffers)
|
||||
}
|
57
common/bufio/vectorised_unix.go
Normal file
57
common/bufio/vectorised_unix.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
//go:build !windows
|
||||
|
||||
package bufio
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (w *SyscallVectorisedWriter) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
defer buf.ReleaseMulti(buffers)
|
||||
iovecList := make([]unix.Iovec, 0, len(buffers))
|
||||
for _, buffer := range buffers {
|
||||
var iovec unix.Iovec
|
||||
iovec.Base = &buffer.Bytes()[0]
|
||||
iovec.SetLen(buffer.Len())
|
||||
iovecList = append(iovecList, iovec)
|
||||
}
|
||||
var innerErr unix.Errno
|
||||
err := w.rawConn.Write(func(fd uintptr) (done bool) {
|
||||
_, _, innerErr = unix.Syscall(unix.SYS_WRITEV, fd, uintptr(unsafe.Pointer(&iovecList[0])), uintptr(len(iovecList)))
|
||||
return innerErr != unix.EAGAIN && innerErr != unix.EWOULDBLOCK
|
||||
})
|
||||
if innerErr != 0 {
|
||||
err = innerErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *SyscallVectorisedPacketWriter) WriteVectorisedPacket(buffers []*buf.Buffer, destination M.Socksaddr) error {
|
||||
iovecList := make([]unix.Iovec, 0, len(buffers))
|
||||
for _, buffer := range buffers {
|
||||
var iovec unix.Iovec
|
||||
iovec.Base = &buffer.Bytes()[0]
|
||||
iovec.SetLen(buffer.Len())
|
||||
iovecList = append(iovecList, iovec)
|
||||
}
|
||||
name, nameLen := destination.Sockaddr()
|
||||
var msgHdr unix.Msghdr
|
||||
msgHdr.Name = (*byte)(name)
|
||||
msgHdr.Namelen = nameLen
|
||||
msgHdr.Iov = &iovecList[0]
|
||||
msgHdr.SetIovlen(len(iovecList))
|
||||
var innerErr unix.Errno
|
||||
err := w.rawConn.Write(func(fd uintptr) (done bool) {
|
||||
_, _, innerErr = unix.Syscall(unix.SYS_SENDMSG, fd, uintptr(unsafe.Pointer(&msgHdr)), 0)
|
||||
return innerErr != unix.EAGAIN && innerErr != unix.EWOULDBLOCK
|
||||
})
|
||||
if innerErr != 0 {
|
||||
err = innerErr
|
||||
}
|
||||
return err
|
||||
}
|
50
common/bufio/vectorised_windows.go
Normal file
50
common/bufio/vectorised_windows.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package bufio
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (w *SyscallVectorisedWriter) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
defer buf.ReleaseMulti(buffers)
|
||||
iovecList := make([]*windows.WSABuf, len(buffers))
|
||||
for i, buffer := range buffers {
|
||||
iovecList[i] = &windows.WSABuf{
|
||||
Len: uint32(buffer.Len()),
|
||||
Buf: &buffer.Bytes()[0],
|
||||
}
|
||||
}
|
||||
var n uint32
|
||||
var innerErr error
|
||||
err := w.rawConn.Write(func(fd uintptr) (done bool) {
|
||||
innerErr = windows.WSASend(windows.Handle(fd), iovecList[0], uint32(len(iovecList)), &n, 0, nil, nil)
|
||||
return innerErr != windows.WSAEWOULDBLOCK
|
||||
})
|
||||
if innerErr != nil {
|
||||
err = innerErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *SyscallVectorisedPacketWriter) WriteVectorisedPacket(buffers []*buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buf.ReleaseMulti(buffers)
|
||||
iovecList := make([]*windows.WSABuf, len(buffers))
|
||||
for i, buffer := range buffers {
|
||||
iovecList[i] = &windows.WSABuf{
|
||||
Len: uint32(buffer.Len()),
|
||||
Buf: &buffer.Bytes()[0],
|
||||
}
|
||||
}
|
||||
var n uint32
|
||||
var innerErr error
|
||||
err := w.rawConn.Write(func(fd uintptr) (done bool) {
|
||||
innerErr = windows.WSASendto(windows.Handle(fd), iovecList[0], uint32(len(iovecList)), &n, 0, destination.Sockaddr(), nil, nil)
|
||||
return innerErr != windows.WSAEWOULDBLOCK
|
||||
})
|
||||
if innerErr != nil {
|
||||
err = innerErr
|
||||
}
|
||||
return err
|
||||
}
|
31
common/metadata/addr_posix.go
Normal file
31
common/metadata/addr_posix.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
//go:build !windows
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (ap Socksaddr) Sockaddr() (name unsafe.Pointer, nameLen uint32) {
|
||||
if ap.IsFqdn() {
|
||||
panic("bad sockaddr")
|
||||
} else if ap.IsIPv4() {
|
||||
rsa4 := syscall.RawSockaddrInet4{
|
||||
Family: syscall.AF_INET,
|
||||
Port: ap.Port,
|
||||
Addr: ap.Addr.As4(),
|
||||
}
|
||||
name = unsafe.Pointer(&rsa4)
|
||||
nameLen = syscall.SizeofSockaddrInet4
|
||||
} else {
|
||||
rsa6 := syscall.RawSockaddrInet6{
|
||||
Family: syscall.AF_INET6,
|
||||
Port: ap.Port,
|
||||
Addr: ap.Addr.As16(),
|
||||
}
|
||||
name = unsafe.Pointer(&rsa6)
|
||||
nameLen = syscall.SizeofSockaddrInet6
|
||||
}
|
||||
return
|
||||
}
|
21
common/metadata/addr_windows.go
Normal file
21
common/metadata/addr_windows.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package metadata
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (ap Socksaddr) Sockaddr() windows.Sockaddr {
|
||||
if ap.IsFqdn() {
|
||||
panic("bad sockaddr")
|
||||
} else if ap.IsIPv4() {
|
||||
return &windows.SockaddrInet4{
|
||||
Port: int(ap.Port),
|
||||
Addr: ap.Addr.As4(),
|
||||
}
|
||||
} else {
|
||||
return &windows.SockaddrInet6{
|
||||
Port: int(ap.Port),
|
||||
Addr: ap.Addr.As16(),
|
||||
}
|
||||
}
|
||||
}
|
14
common/network/vectorised.go
Normal file
14
common/network/vectorised.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
type VectorisedWriter interface {
|
||||
WriteVectorised(buffers []*buf.Buffer) error
|
||||
}
|
||||
|
||||
type VectorisedPacketWriter interface {
|
||||
WriteVectorisedPacket(buffers []*buf.Buffer, destination M.Socksaddr) error
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
// Deprecated: use vectorised writer
|
||||
func WriteV(fd uintptr, data [][]byte) (int, error) {
|
||||
iovecs := make([]syscall.Iovec, len(data))
|
||||
for i := range iovecs {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
// Deprecated: use vectorised writer
|
||||
func WriteV(fd uintptr, data [][]byte) (int, error) {
|
||||
var n uint32
|
||||
buffers := make([]*syscall.WSABuf, len(data))
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
// +----+------+------+----------+----------+----------+
|
||||
|
||||
type AssociatePacketConn struct {
|
||||
N.PacketConn
|
||||
N.NetPacketConn
|
||||
addr net.Addr
|
||||
remoteAddr M.Socksaddr
|
||||
underlying net.Conn
|
||||
|
@ -25,17 +25,17 @@ type AssociatePacketConn struct {
|
|||
|
||||
func NewAssociatePacketConn(conn net.PacketConn, remoteAddr M.Socksaddr, underlying net.Conn) *AssociatePacketConn {
|
||||
return &AssociatePacketConn{
|
||||
PacketConn: bufio.NewPacketConn(conn),
|
||||
remoteAddr: remoteAddr,
|
||||
underlying: underlying,
|
||||
NetPacketConn: bufio.NewPacketConn(conn),
|
||||
remoteAddr: remoteAddr,
|
||||
underlying: underlying,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAssociateConn(conn net.Conn, remoteAddr M.Socksaddr, underlying net.Conn) *AssociatePacketConn {
|
||||
return &AssociatePacketConn{
|
||||
PacketConn: bufio.NewUnbindPacketConn(conn),
|
||||
remoteAddr: remoteAddr,
|
||||
underlying: underlying,
|
||||
NetPacketConn: bufio.NewUnbindPacketConn(conn),
|
||||
remoteAddr: remoteAddr,
|
||||
underlying: underlying,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ func (c *AssociatePacketConn) RemoteAddr() net.Addr {
|
|||
//warn:unsafe
|
||||
func (c *AssociatePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
buffer := buf.With(p)
|
||||
n, _, err = bufio.ReadFrom(c.PacketConn, buffer)
|
||||
n, _, err = bufio.ReadFrom(c.NetPacketConn, buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func (c *AssociatePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
return bufio.WriteTo(c.PacketConn, buffer, c.addr)
|
||||
return bufio.WriteTo(c.NetPacketConn, buffer, c.addr)
|
||||
}
|
||||
|
||||
func (c *AssociatePacketConn) Read(b []byte) (n int, err error) {
|
||||
|
@ -89,7 +89,7 @@ func (c *AssociatePacketConn) Write(b []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func (c *AssociatePacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||
_, addr, err := bufio.ReadFrom(c.PacketConn, buffer)
|
||||
_, addr, err := bufio.ReadFrom(c.NetPacketConn, buffer)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
}
|
||||
|
@ -103,11 +103,11 @@ func (c *AssociatePacketConn) WritePacket(buffer *buf.Buffer, destination M.Sock
|
|||
header := buf.With(buffer.ExtendHeader(3 + M.SocksaddrSerializer.AddrPortLen(destination)))
|
||||
common.Must(header.WriteZeroN(3))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
return common.Error(bufio.WriteTo(c.PacketConn, buffer, c.addr))
|
||||
return common.Error(bufio.WriteTo(c.NetPacketConn, buffer, c.addr))
|
||||
}
|
||||
|
||||
func (c *AssociatePacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
return c.NetPacketConn
|
||||
}
|
||||
|
||||
func (c *AssociatePacketConn) FrontHeadroom() int {
|
||||
|
@ -116,7 +116,7 @@ func (c *AssociatePacketConn) FrontHeadroom() int {
|
|||
|
||||
func (c *AssociatePacketConn) Close() error {
|
||||
return common.Close(
|
||||
c.PacketConn,
|
||||
c.NetPacketConn,
|
||||
c.underlying,
|
||||
)
|
||||
}
|
||||
|
|
54
protocol/socks/packet_vectorised.go
Normal file
54
protocol/socks/packet_vectorised.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package socks
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ N.VectorisedPacketWriter = (*VectorisedAssociatePacketConn)(nil)
|
||||
|
||||
type VectorisedAssociatePacketConn struct {
|
||||
AssociatePacketConn
|
||||
N.VectorisedPacketWriter
|
||||
}
|
||||
|
||||
func NewVectorisedAssociatePacketConn(conn net.PacketConn, writer N.VectorisedPacketWriter, remoteAddr M.Socksaddr, underlying net.Conn) *VectorisedAssociatePacketConn {
|
||||
return &VectorisedAssociatePacketConn{
|
||||
AssociatePacketConn{
|
||||
NetPacketConn: bufio.NewPacketConn(conn),
|
||||
remoteAddr: remoteAddr,
|
||||
underlying: underlying,
|
||||
},
|
||||
writer,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVectorisedAssociateConn(conn net.Conn, writer N.VectorisedWriter, remoteAddr M.Socksaddr, underlying net.Conn) *VectorisedAssociatePacketConn {
|
||||
return &VectorisedAssociatePacketConn{
|
||||
AssociatePacketConn{
|
||||
NetPacketConn: bufio.NewUnbindPacketConn(conn),
|
||||
remoteAddr: remoteAddr,
|
||||
underlying: underlying,
|
||||
},
|
||||
&bufio.UnbindVectorisedPacketWriter{VectorisedWriter: writer},
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VectorisedAssociatePacketConn) WriteVectorisedPacket(buffers []*buf.Buffer, destination M.Socksaddr) error {
|
||||
_header := buf.StackNewSize(3 + M.SocksaddrSerializer.AddrPortLen(destination))
|
||||
defer common.KeepAlive(_header)
|
||||
header := common.Dup(_header)
|
||||
defer header.Release()
|
||||
common.Must(header.WriteZeroN(3))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
return v.VectorisedPacketWriter.WriteVectorisedPacket(append([]*buf.Buffer{header}, buffers...), destination)
|
||||
}
|
||||
|
||||
func (c *VectorisedAssociatePacketConn) FrontHeadroom() int {
|
||||
return 0
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue