Add vectorised interface

This commit is contained in:
世界 2022-08-12 13:46:55 +08:00
parent 13f394e202
commit 05f9836bff
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
12 changed files with 371 additions and 13 deletions

View file

@ -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

View file

@ -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
View 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)
}

View 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
}

View 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
}

View 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
}

View 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(),
}
}
}

View 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
}

View file

@ -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 {

View file

@ -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))

View file

@ -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,
)
}

View 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
}